Blog
How to split shared cloud costs between teams
Chargeback starts easy. Most line items on a cloud bill already belong to somebody: this database is the payments team’s, that GPU node pool is research’s, those S3 buckets are the data platform’s. You tag them, you group by the tag, and you have a per-team number.
Then you reach the rest of the bill. The shared Kubernetes cluster four teams deploy onto. The NAT gateway everything egresses through. The observability bill, which is one line item and covers everyone. Together this is routinely 20-40% of the total, and no tag will ever assign it, because it isn’t one team’s.
Three ways people handle it
Leave it in an “Untagged” bucket. Honest, and useless. Every team’s reported cost is now understated by a different, unknown amount, so the numbers can’t be compared to each other or to a budget. The bucket also grows, because nobody owns reducing it.
Split it evenly. This charges the smallest team the same as the largest for a cluster they barely touch. The first time someone notices, they stop believing the whole report.
Split it by usage. The one that needs real machinery: a ratio you can defend, and totals that still reconcile with the invoice.
Split rules and redistribution
Split rules and proportional redistribution solve different problems. Use one where the other belongs and your per-team totals stop matching the invoice.
Split rules are for spend you can identify but can’t assign to one owner. A shared cluster is a known line item; it just has four claimants. So a tag rule can carry weights instead of a single owner — 40% platform, 30% payments, 20% search, 10% data — and every matching dollar is divided along them. You agree the ratio up front rather than defending it afterwards.
Proportional redistribution is for spend you can’t identify at all. Rather than assigning the leftovers by hand, you dissolve the whole Untagged bucket into the others in proportion to what each has already been attributed. A team that accounts for 30% of the attributable spend absorbs 30% of the unattributable. The residue shrinks to nothing without anyone maintaining a rule for it.
Use split rules when you know what the cost is and who might share it. Use redistribution for the remainder you’ll never chase down.

Those two rows cover the same $1.04M of spend and have roughly the same number of rules.
customer accounts for 22% of it and leaves the rest sitting in a bucket nobody owns.
team dissolves that bucket and reaches 100%.
Not by tagging more, but by allocating what could never be tagged.
Totals have to reconcile
Whatever you do here, one property has to hold:
The sum of every team’s cost for a day must equal the total cost for that day.
Splitting preserves it because the weights sum to 1. Redistribution preserves it because it only moves dollars between buckets. It never creates or destroys any. If a tool lets you configure an allocation where those two numbers diverge, the per-team report and the invoice will disagree.
It’s easy to build an allocation feature that produces plausible per-team numbers which don’t add up to the bill, so check it yourself in whatever you use.
What “proportionally” is measured over
Redistribution only goes wrong once you have more than one provider.
To redistribute proportionally, you need a basis: each team’s already-attributed spend. Then you have to pick a time unit to measure that basis over. The obvious choice is “the finest granularity available.” That choice is wrong, and it fails silently.
Cost data doesn’t arrive at a uniform granularity, and not because the providers can’t supply
it. AWS Cost Explorer serves hourly. GCP’s billing export carries a sub-daily
usage_start_time, and Snowflake’s warehouse metering history is hourly too. Plenty of spend has
no sub-daily shape at all, though: a SaaS subscription, a monthly committed-use discount, a
hand-entered cost line. Whatever you ingest, and at whatever grain you store it, the sources you
redistribute across won’t share a common unit finer than a day.
So if you compute the basis per raw timestamp, a GCP row you’ve stored at daily grain lands in exactly one of AWS’s twenty-four hourly buckets. In that one hour it’s present. In the other twenty-three it doesn’t exist. AWS-attributed spend is present in all twenty-four. The result: AWS absorbs almost the entire shared cost, and every other provider is let off nearly free. Not because of anything real, but because of the grain each source landed at.
The answer isn’t the finest granularity. It’s the coarsest unit every source shares, which is the day. An hour means the same thing for AWS and for Snowflake, but it means nothing for a monthly subscription line, and it isn’t what you’ll have stored for every source anyway. A day means the same thing for all of them. Plutus redistributes per UTC day, and conservation is a per-day property.
View an hourly chart and you’re looking at something finer than the allocation unit. The day’s shared cost gets spread back across that day’s hours in proportion to each team’s own hourly spend, so the overhead lands in the hours a team was actually working. An hourly view therefore reconciles across the day rather than within each hour.
What to actually do
- Tag the spend that has a clear owner first. Don’t reach for allocation machinery until the easy 60-80% is assigned.
- Use weighted splits for shared line items you can name, and agree the weights with the teams before the first report, not after.
- Turn on redistribution for the remainder, so the Untagged bucket stops being a growing asterisk.
- Check that a team’s total still reconciles to the invoice. If it doesn’t, the allocation is wrong however the rules are set up.
The mechanics, including how rule changes apply retroactively and how this interacts with per-team budgets, are in the virtual tagging documentation.
Get started free. No credit card, no sales call.