Building Multi-Tenant SaaS Applications on AWS
Multi-tenancy is mostly a set of trade-offs between isolation, cost and how much operational work you are willing to sign up for. I architected several multi-tenant platforms at Pivotree, and these are the patterns I kept coming back to.
Three isolation models
Silo: dedicated resources per tenant
Every tenant gets its own infrastructure. Strongest isolation, highest cost, and the deployment story gets harder with each tenant you add.
┌─────────────────┐ ┌─────────────────┐
│ Tenant A │ │ Tenant B │
│ ┌───────────┐ │ │ ┌───────────┐ │
│ │ Lambda │ │ │ │ Lambda │ │
│ │ DynamoDB │ │ │ │ DynamoDB │ │
│ │ S3 │ │ │ │ S3 │ │
│ └───────────┘ │ │ └───────────┘ │
└─────────────────┘ └─────────────────┘
Pool: shared resources
All tenants share infrastructure and are separated logically. Cheapest to run, and every isolation guarantee now lives in your code, which is where bugs live too.
Bridge: shared compute, isolated data
Shared compute with separate data stores. This is where most of the platforms I worked on ended up, because data isolation is the part auditors ask about and compute isolation is the part that costs money.
The serverless version
For a bridge model on AWS:
- API Gateway as a shared entry point with tenant routing.
- Lambda for shared compute, with tenant context injected per invocation.
- DynamoDB with table-per-tenant or partition key isolation, depending on tenant size.
- Cognito for tenant-aware authentication.
Tenant context is worth getting right early, because everything downstream depends on it being present and correct:
// Middleware to inject tenant context
export const tenantMiddleware = async (event) => {
const tenantId = extractTenantId(event);
const tenantConfig = await getTenantConfig(tenantId);
return {
...event,
tenantContext: {
id: tenantId,
tier: tenantConfig.tier,
dataPartition: tenantConfig.dataPartition,
}
};
};
The four things that bit us
Noisy neighbours. One tenant’s batch job will find your shared throughput limit. Per-tenant throttling and quotas, before you need them.
Data isolation in logs. Application data is usually partitioned carefully and then leaked through a log line or an error report. Logs count as tenant data.
Deployment blast radius. Rolling out to every tenant at once removes the main advantage of having tiers. Canary per tenant tier instead.
Cost attribution. Tag everything from day one. Working out per-tenant cost retroactively is a data archaeology project.
Multi-tenancy adds real complexity, and most of it shows up in operations rather than in the architecture diagram. Deciding where the isolation boundary sits, and then holding that line, is most of the job.