← Blog

SaaS · Field guide

Cost per customer in a multi-tenant SaaS on AWS

Your AWS bill is part of cost of goods sold, yet most SaaS teams cannot say what each customer costs to serve. Here is how to measure it when customers share the same infrastructure.

By Ribau · · 8 min read

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.

ModelWhat it looks likeHow cost is attributed
SiloA dedicated stack or account per customer, common for large or regulated customersDirectly from the bill, by account or tag
PoolAll customers share the same services, databases and queuesBy usage: requests, storage, compute time or events per tenant
BridgeShared application layer with some dedicated resources, such as a database per customerDirect 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.

CategoryExamplesTreatment
Dedicated tenant resourcesSilo accounts, per-tenant databases, dedicated clustersAssign directly to the tenant
Pooled product infrastructureShared API, compute, databases, queues, storageAttribute by measured usage (step 2)
Shared platformNetworking, observability, security tooling, CI/CD, supportAllocate with a written rule (step 3)
Non-production and R&DDevelopment, test, staging, experimentsExclude 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.

ComponentGood first driverWhere the data usually comes from
API and application computeRequests or compute time per tenantApplication logs or API gateway access logs with a tenant ID
Shared relational databaseQuery time or rows read and written per tenantApplication metrics; database statistics sampled by tenant
Object storageBytes stored per tenantA tenant prefix per bucket path and S3 Storage Lens or inventory reports
Queues and streamsMessages per tenantProducer-side counters
Search and analyticsQueries and indexed documents per tenantApplication metrics
AI featuresTokens or model calls per tenantThe 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.

LineMonthly 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:

SegmentCustomersShare of usageCost to serve per customer
Enterprise (dedicated + pooled)520%about $6,700
Mid-market9550%about $410
Self-serve30030%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

CauseWhat you seeWhat usually fixes it
Silo by defaultEvery large deal gets its own stack, and each stack runs at low utilizationSilo only where contracts or regulation require it; price it accordingly
Noisy neighborsA few tenants drive most of the pooled cost and degrade everyone elsePer-tenant quotas and throttling; usage-based pricing for heavy features
Unbounded data retentionStorage and query cost grow with every customer's historyRetention by plan; archive tiers; purge on contract end
Features with hidden unit costNew capabilities, especially AI features, add cost per request nobody pricedCost per request measured before launch; limits by plan
Free and trial tenantsInactive accounts keep consuming storage and background jobsLifecycle rules for inactive tenants
Over-provisioned shared servicesPooled cost flat while usage swingsScaling 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:

  1. Gross margin by segment and plan, against target.
  2. The ten most expensive customers to serve, with what they pay.
  3. Cost per customer for new features in their first months.
  4. 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.

Try it with your numbers

Estimate AI feature cost per request

Where to start

Cloud Health, FinOps & Governance Review

Where we look first →