Programming

Stop Pretending Your AWS Bill is Honest: The Database Chargeback Problem I've Been Ignoring

A

Admin User

Author

Jul 2, 2026
5 min read
12 views
Stop Pretending Your AWS Bill is Honest: The Database Chargeback Problem I've Been Ignoring

I got an email last month from our finance team asking which team owned the $4,200 RDS bill. I stared at it for a solid minute before responding with something honest: "I don't know, and I don't think anyone does." We run four separate applications on a single SQL Server instance, each team has its own P&L, and yet the bill lands as one line item. I've been shipping code for almost a decade, and this is one of those problems I keep running into that somehow doesn't feel like a "real" technical problem—so it never gets fixed.

But the more I think about it, the more this bothers me. We're engineers. We ship systems that need to be measurable, reproducible, and fair. Yet we're comfortable with "split the bill four ways and call it done" when it comes to infrastructure costs. That's not engineering. That's giving up.

The Blind Spot We All Live With

Here's the thing about AWS billing: it's actually too honest. Your bill is exactly correct—down to the millisecond of compute time, the gigabyte of storage used, the I/O operations performed. But it's honest at the wrong level.

The moment you consolidate multiple databases onto a single RDS instance for cost efficiency (which is sensible, by the way), you lose all granularity. AWS sees one instance. One bill. One tenant from their perspective. They have no idea that inside that instance, you're running AcmePayroll hammering CPU while AcmeReporting sits there fat with data but idle most of the time. They can't tell you, because the database engine keeps those secrets.

So your infrastructure bill becomes undefendable. Finance can't allocate it fairly. Team leads can't understand their actual cost-to-serve. And if one database is genuinely causing problems—the infamous "noisy neighbor" eating resources—you can feel it degrading performance, but you can't prove where the cost is actually going.

Why the Obvious Fix Doesn't Work

The instinct is obvious: give every database its own instance. Clean accounting, clear blast radius. The problem is you've just demolished your unit economics. You lose all the amortization that made shared instances make sense in the first place. Your bill doubles, and your finance team loses it.

The reason this problem is hard is subtler than it looks. You can't just "split by usage" because usage isn't a single dimension. One database might be storage-heavy but CPU-light. Another might pin every CPU cycle while barely touching disk. If you split purely by storage, the busy transactional database looks cheap. If you split by CPU, the archive gets overcharged for capacity it never used.

It gets worse. Some metrics are cumulative (CPU time, I/O operations), which need to be tracked as deltas between readings. Others are point-in-time gauges (memory, disk space) that need averaging. Mix them up and your "fair" split quietly drifts over weeks.

Actually Measuring What's Happening

The approach that makes sense to me—and what HiFX did with DBSteward—is to measure consumption inside the database engine, where the truth actually lives. Don't wait for AWS to tell you. Ask the database directly through its instrumentation.

SQL Server (and most serious databases) expose detailed per-database metrics through DMVs: CPU usage per database, I/O stalls, memory footprint, storage consumed. You can sample these periodically and build a real picture of consumption over time.

The clever part is how you weight these metrics. You don't arbitrarily decide "CPU counts for 50%, storage for 30%"—instead, you weight them based on what's actually constraining your instance. If you're CPU-bound, CPU carries more weight in the split. If you're I/O-bound, I/O dominates. This adjusts automatically as workload patterns change.

My Take: This Should Be Standard Practice

I'm genuinely frustrated this isn't built into RDS as a first-class feature. AWS has all this data. They could expose per-database cost attribution natively. Instead, organizations either pay for third-party tooling or—more commonly—just live with the blindness.

For my team, I'm going to push for implementing something like this. Not necessarily DBSteward specifically, but something that gives us honest numbers about where our money actually goes. Because right now, when finance asks which team should pay the extra $200 this month, I'm just guessing.

The discipline here also matters. Whatever you can't attribute gets reported explicitly as "overhead." No sneaking untracked databases onto someone else's bill. That honesty is actually more valuable than the chargeback itself.

What Would You Do?

Are you running multi-database instances? How are you actually splitting the bill? Or like me, are you pretending this problem doesn't exist until someone in finance asks a question you can't answer?


Source: This post was inspired by "The AWS Bill Says $4,000. It Won't Tell You Which Database Owes What." by Dev.to. Read the original article

Share this article

Written by Adil Sher

Full stack developer building high-traffic platforms, AI services, and custom web applications. Explore my portfolio, learn about my background, or get in touch.

Related Articles

Code Isn't Engineering Until You Stop Thinking It Is
Programming Jul 17

Code Isn't Engineering Until You Stop Thinking It Is

Someone asked me last week what I actually do for a living. Not in the polite "oh that's nice" way—genuinely curious. I said "I'm a developer" and watched their face go blank. They nodded like I'd said "I work in an office." It hit me that I couldn't explain my job any better tha...

PHP Forms Aren't Broken—Your Expectations Are
Programming Jul 16

PHP Forms Aren't Broken—Your Expectations Are

I spent two hours last week debugging a form that "wasn't working." The client said data wasn't being saved. I pulled up the network tab, saw the POST request going out clean, checked the database, and found... nothing. Then I looked at the form's action attribute. It was pointin...