The AWS Bill Says $4,000. It Won't Tell You Which Database Owes What.
The Problem: Cloud Bills Are Blind Below the Instance Line
AWS RDS - and most managed database services - bill at the instance level. A single shared SQL Server instance shows up as one line item on your bill, no matter whether it's hosting two databases or twenty. The cloud provider has no idea that AcmeSales, AcmeInventory, AcmeHR, and AcmePayroll are four different teams with four different budgets. To AWS, they're one tenant of one box, and the box costs what the box costs.
For a lot of organizations this is fine right up until it isn't. The moment it stops being fine usually looks like one of these:
- Finance wants charge back - Each team or cost center is supposed to pay for what it uses. But the bill stops at the instance, so the cost gets split by a crude proxy - headcount, "everyone pays an equal quarter," or a number someone made up in a spreadsheet two years ago that nobody can defend anymore.
- Noisy neighbor shows up - One database is hammering CPU and I/O, dragging performance for everyone else on the instance. You can feel it. You just can't prove it, because the metrics that would prove it live inside the database engine, not on the bill.
- SaaS margins get fuzzy - If you run a multi-tenant product where tenants share infrastructure, you genuinely don't know your cost-to-serve per tenant - which means you don't really know your margins either.
The instinctive fix is to give every database its own instance. Clean billing, clean blast radius - and a much larger bill, because you've traded the entire economic point of consolidation (shared, amortized capacity) for accounting convenience. Most teams correctly refuse to do that. So they live with the blindness instead.
Why This Is Harder Than It Looks
The naive answer is "just split the bill by usage." Fine - but which usage, and measured how?
A reporting database like AcmeInventory might sit nearly idle on CPU while occupying half the disk. An OLTP database like AcmeSales might barely touch disk while pinning CPU and saturating the I/O budget. If you split the bill purely by storage, the busy little database pays almost nothing and the quiet large one carries everyone. Split it purely by CPU and you get the opposite injustice. There's no single resource that's "the fair one" - fairness depends on what the instance is actually constrained on.
There are subtler traps too. Some metrics are counters that only ever climb (CPU time, cumulative I/O waits); some are gauges you read at a moment in time (memory, disk, log size). You can't treat them the same way. Counters have to be the difference between readings and watched for resets; gauges have to be averaged over the period. Get this wrong and your "fair split" quietly drifts.
And critically: the databases you're tracking rarely add up to the whole instance. System databases, tempdb, background overhead, and any database you haven't onboarded all consume real resources. If your model forces the tracked databases to absorb 100% of the bill, you're overcharging them for capacity they never touched.
The Solution: DBSteward Measures Consumption Inside the Engine, Then Apportions
HiFX built DBSteward for exactly this gap. The approach is to stop trying to get billing granularity from the billing system, and instead get it from the database engine, where the truth actually lives. The idea in three moves:
Instrument What the Instance Is Actually Doing
SQL Server exposes detailed per-database resource consumption through dynamic management views (DMVs). DBSteward reads these on a schedule - CPU, I/O stalls, memory footprint, data and log size - capturing per-database usage plus the instance total, period over period. No agents inside tenant databases, no CloudWatch dependency, no Lambda. Just measurement.
Blend the Metrics Into a Fair, Defensible Share
Each metric becomes a share: this database's usage divided by the instance total for that metric. Those shares are combined using weights - how much each kind of resource should count. A disk-heavy archive and a CPU-heavy transactional database can be judged on the dimensions that actually matter for each.
The weights can be set by an administrator as policy, or - the part most customers end up preferring - computed automatically from what the instance itself is constrained on. If the instance is genuinely CPU-bound this period, CPU naturally carries more of the split, without anyone hand-tuning numbers.
Apportion the Bill - and Leave the Remainder Honest
Each database's blended score becomes its fraction of the instance bill. The crucial discipline is that the tracked databases don't have to consume the whole bill. Whatever isn't attributable - system overhead, untracked databases, the shared baseline - is reported as an explicit Unallocated line. The numbers reconcile to the bill exactly, every time, with nothing silently shoved onto whoever happened to be measured.
A Worked Example
Take a shared instance, prd-acme-rds-sql1, that cost $1,000 for the month. Two databases are tracked - AcmeSales (busy, transactional) and AcmeInventory (large, quiet) - and DBSteward has the instance totals for every metric.
For simplicity, three metrics are in play. First, the measured usage over the period:
| Metric | AcmeSales | AcmeInventory | Instance total |
|---|---|---|---|
| CPU (s) | 300 | 100 | 1,000 |
| I/O stall (ms) | 200,000 | 200,000 | 500,000 |
| Disk (GB avg) | 50 | 150 | 400 |
Each usage figure becomes a share of the instance total:
| Metric | AcmeSales share | AcmeInventory share |
|---|---|---|
| CPU | 300 / 1,000 = 0.30 | 100 / 1,000 = 0.10 |
| I/O Throttle | 200k / 500k = 0.40 | 200k / 500k = 0.40 |
| Disk Usage | 50 / 400 = 0.125 | 150 / 400 = 0.375 |
Now the weights. AcmeSales is judged mostly on compute; AcmeInventory mostly on storage. Each database's weights total 100%:
| Metric | AcmeSales weight | AcmeInventory weight |
|---|---|---|
| CPU | 50% | 20% |
| I/O Throttle | 30% | 20% |
| Disk Usage | 20% | 60% |
Each database's score is the weighted sum of its shares:
score(AcmeSales)= 0.50×0.30 + 0.30×0.40 + 0.20×0.125 = 0.295score(AcmeInventory)= 0.20×0.10 + 0.20×0.40 + 0.60×0.375 = 0.325
Because we have full instance totals, those scores are each database's fraction of the bill:
| Line item | Fraction | Cost | Note |
|---|---|---|---|
| AcmeSales | 29.5% | $295.00 | Weighted blend of measured shares |
| AcmeInventory | 32.5% | $325.00 | Weighted blend of measured shares |
| Unallocated | 38.0% | $380.00 | System databases + untracked + shared overhead |
| Total | 100% | $1,000.00 | Reconciles to the bill exactly |
The two tracked databases together don't absorb the whole bill - the $380 that belongs to system databases and shared baseline is reported explicitly, not quietly redistributed onto the teams that happened to be measured. That's what makes the number defensible when you put it in front of finance: here is what you used, here is how it was weighted, here is your share of a real AWS bill, and here is the part that belongs to nobody in particular.
Why It Matters
Cost attribution sounds like an accounting chore, but the inability to do it shapes real engineering decisions - usually badly. When you can't measure per-database cost, you can't have an honest conversation about consolidation. Teams over-provision dedicated instances purely so the billing is clean, burning the savings that shared infrastructure was supposed to deliver. Noisy neighbors go unaddressed because nobody can attribute the pain. SaaS pricing gets set on vibes because true cost-to-serve is unknown. And charge back - the mechanism that's supposed to make teams cost-aware - becomes a flat tax that teaches everyone to ignore it.
Closing the gap between "the instance costs $4,000" and "here's the defensible $4,000 split" turns cost from a monthly surprise into a signal. It makes consolidation safe to pursue, makes noisy neighbors visible, and makes margins knowable. That's the whole point: not a prettier bill, but the ability to act on it.
DBSteward is a FinOps tool from HiFX for per-database cost visibility and charge back across shared AWS RDS SQL Server instances. Learn more at www.dbsteward.com.
Comments
No comments yet. Start the discussion.