Why cost per customer matters
For a SaaS company, the AWS bill is part of cost of goods sold. It decides gross margin, and gross margin decides how much the business can spend on growth. Yet most SaaS teams can say what AWS costs in total and almost nothing about what each customer costs to serve.
That gap shows up in three places. Pricing: tiers are set without knowing whether the largest customers are the most profitable or the least. Sales: enterprise deals with heavy usage get discounted further, on top of costing more to run. Engineering: nobody can tell whether a new feature or a new customer segment improves margin or erodes it.
The method is the same one we use for cost per transaction in payments, with one extra difficulty. In most SaaS platforms, customers share the same infrastructure, so their cost cannot be read from the bill. It has to be attributed.
Know your tenancy model
How customers share infrastructure decides how hard it is to measure their cost. Most platforms combine more than one model.
| Model | What it looks like | How cost is attributed |
|---|---|---|
| Silo | A dedicated stack or account per customer, common for large or regulated customers | Directly from the bill, by account or tag |
| Pool | All customers share the same services, databases and queues | By usage: requests, storage, compute time or events per tenant |
| Bridge | Shared application layer with some dedicated resources, such as a database per customer | Direct for the dedicated parts, usage-based for the shared parts |
The AWS Well-Architected SaaS Lens describes these models in detail. For cost, the practical point is that silo costs are easy and expensive, while pool costs are efficient and need instrumentation.
Step 1: separate tenant cost from platform cost
Start from the same split as any unit-cost work. Group spend into categories with AWS accounts and a small set of cost allocation tags, then query the Cost and Usage Report with Amazon Athena.
| Category | Examples | Treatment |
|---|---|---|
| Dedicated tenant resources | Silo accounts, per-tenant databases, dedicated clusters | Assign directly to the tenant |
| Pooled product infrastructure | Shared API, compute, databases, queues, storage | Attribute by measured usage (step 2) |
| Shared platform | Networking, observability, security tooling, CI/CD, support | Allocate with a written rule (step 3) |
| Non-production and R&D | Development, test, staging, experiments | Exclude from cost of goods sold; track separately |
The last row matters for margin. Development environments are a real cost, but they are research and development, not cost of serving customers. Mixing them in makes gross margin look worse than it is and hides the trend you are trying to see.
If you run in AWS Organizations, a tag policy keeps the tenant tag spelled the same way everywhere. A minimal version looks like this:
{
"tags": {
"tenant": {
"tag_key": { "@@assign": "tenant" },
"enforced_for": { "@@assign": ["dynamodb:table", "s3:bucket"] }
}
}
}
Note what it does and doesn't do: it stops people from writing Tenant or tenant_id on the listed resource types, but it does not force anyone to tag a new resource. Pair it with a check in your infrastructure-as-code pipeline.
Step 2: attribute pooled cost with usage
Pooled resources cannot be tagged by tenant, so their cost is divided in proportion to what each tenant uses. That needs one thing most platforms lack: usage events that carry a tenant identifier.
- Emit usage events from the application. Each request, job or stored object records the tenant ID and a measure such as duration, bytes or rows. Write them to a stream or log, aggregate them daily into a usage table.
- Pick one driver per pooled component. Requests or compute time for the API layer, storage and I/O for databases, messages for queues, bytes for object storage.
- Use what AWS already splits. Split cost allocation data for Amazon ECS and Amazon EKS divides shared cluster cost by task or pod. If tenants map to namespaces or services, that does part of the work for you.
- Accept approximation. A driver that explains most of a component's cost is good enough. The goal is a decision-quality number, not an invoice.
Tenant share of a pooled component = component cost × tenant usage ÷ total usage
Step 3: allocate the shared platform
Shared platform costs are allocated with a simple, written rule, usually in proportion to each tenant's direct and pooled cost. AWS Cost Categories can apply split charge rules for the platform portion, so finance and engineering see the same numbers in Cost Explorer.
Keep the rule stable. When you change it, record the date and the reason, and restate the previous months if the change is material. A margin trend built on changing rules is not a trend.
What to instrument first
You do not need perfect metering on day one. Start with the components that carry most of the pooled cost and a driver you can already measure, then improve where the numbers are disputed.
| Component | Good first driver | Where the data usually comes from |
|---|---|---|
| API and application compute | Requests or compute time per tenant | Application logs or API gateway access logs with a tenant ID |
| Shared relational database | Query time or rows read and written per tenant | Application metrics; database statistics sampled by tenant |
| Object storage | Bytes stored per tenant | A tenant prefix per bucket path and S3 Storage Lens or inventory reports |
| Queues and streams | Messages per tenant | Producer-side counters |
| Search and analytics | Queries and indexed documents per tenant | Application metrics |
| AI features | Tokens or model calls per tenant | The application layer that calls the model |
Two design choices make this much easier later: put the tenant ID in every log line and metric from the start, and give each tenant a predictable prefix or key in shared storage. Both are cheap in a new service and expensive to retrofit.
A worked example
Take a SaaS platform with 400 customers and an AWS bill of $120,000 a month. These figures are illustrative.
| Line | Monthly cost |
|---|---|
| Non-production and R&D (excluded) | $24,000 |
| Dedicated resources for 5 enterprise customers | $18,000 |
| Pooled product infrastructure | $60,000 |
| Shared platform | $18,000 |
| Cost of serving customers | $96,000 |
The average is $240 per customer per month. The average hides almost everything. Once pooled cost is attributed by usage, the picture typically looks like this:
| Segment | Customers | Share of usage | Cost to serve per customer |
|---|---|---|---|
| Enterprise (dedicated + pooled) | 5 | 20% | about $6,700 |
| Mid-market | 95 | 50% | about $410 |
| Self-serve | 300 | 30% | about $80 |
Pooled and shared cost ($78,000) is split by usage share; dedicated cost goes to the enterprise segment. Compare those figures with what each segment pays and the margin conversation changes. A mid-market plan priced at $600 a month runs at a thin margin on infrastructure alone; a self-serve plan may be healthy; and one enterprise customer may cost more to serve than its contract earns.
Why cost per customer drifts
| Cause | What you see | What usually fixes it |
|---|---|---|
| Silo by default | Every large deal gets its own stack, and each stack runs at low utilization | Silo only where contracts or regulation require it; price it accordingly |
| Noisy neighbors | A few tenants drive most of the pooled cost and degrade everyone else | Per-tenant quotas and throttling; usage-based pricing for heavy features |
| Unbounded data retention | Storage and query cost grow with every customer's history | Retention by plan; archive tiers; purge on contract end |
| Features with hidden unit cost | New capabilities, especially AI features, add cost per request nobody priced | Cost per request measured before launch; limits by plan |
| Free and trial tenants | Inactive accounts keep consuming storage and background jobs | Lifecycle rules for inactive tenants |
| Over-provisioned shared services | Pooled cost flat while usage swings | Scaling on real demand; commitments only for the stable base |
AI features deserve a special mention. A feature that calls a model on every request can cost more per customer than the rest of the platform combined. Estimate it before launch with our generative AI cost calculator, and set limits by plan.
Using the number
Cost per customer earns its keep when it changes decisions. Review it monthly with product, engineering and finance in the room:
- Gross margin by segment and plan, against target.
- The ten most expensive customers to serve, with what they pay.
- Cost per customer for new features in their first months.
- One or two actions, each with an owner and an expected effect on margin.
Over a few months the review becomes the input to pricing decisions, enterprise deal reviews and the architecture roadmap. It is also one of the first things an acquirer or a growth investor will ask to see.
Questions we hear
Do we need per-tenant metering before we start?
No. Start with the categories in step 1 and a rough driver, such as active users or API calls per tenant, from data you already have. Better metering can come later, once the first numbers show where precision matters.
Should we move enterprise customers to dedicated stacks?
Only when a contract, regulation or real isolation need requires it. Dedicated stacks are easy to measure but expensive to run; price them so the customer pays for the isolation.
Is this the same as showback or chargeback?
Related, but the audience differs. Showback and chargeback assign cost to internal teams. Cost per customer assigns it to the people who pay you, which is what pricing and margin decisions need.
How long does it take to set up?
A first version from accounts, tags and existing usage data usually takes a few weeks of part-time work. Proper usage events in the application are a separate, small engineering project.