Multi-Tenancy Architecture: Shared vs Isolated Patterns for SaaS
Every SaaS app serves multiple customers (tenants) from the same infrastructure. The architecture decision — how much isolation between tenants — affects security, cost, performance, and complexity.
The Three Models#
1. Shared Database (Row-Level Isolation)#
All tenants in the same database, same tables. tenant_id column on every row.
SELECT * FROM orders WHERE tenant_id = 'acme' AND status = 'active';
Pros: Cheapest, simplest, easy to manage Cons: Noisy neighbor risk, one bad query affects all, data leak if you forget WHERE clause Best for: Early-stage SaaS, < 1000 tenants, non-sensitive data
2. Schema-Per-Tenant#
Same database server, separate schema (namespace) per tenant.
-- Tenant A
SELECT * FROM tenant_acme.orders WHERE status = 'active';
-- Tenant B
SELECT * FROM tenant_corp.orders WHERE status = 'active';
Pros: Better isolation, easier backup/restore per tenant, no tenant_id risk Cons: Schema migration across N schemas is complex, connection pooling harder Best for: Mid-stage SaaS, 100-10K tenants, moderate isolation needs
3. Database-Per-Tenant#
Each tenant gets their own database instance.
Tenant A → postgres-acme.rds.amazonaws.com
Tenant B → postgres-corp.rds.amazonaws.com
Pros: Maximum isolation, per-tenant scaling, easy compliance (data residency) Cons: Expensive (N databases), complex provisioning, cross-tenant queries impossible Best for: Enterprise SaaS, regulated industries (HIPAA, finance), large tenants
Comparison#
| Factor | Shared DB | Schema-Per-Tenant | DB-Per-Tenant |
|---|---|---|---|
| Cost | Lowest | Medium | Highest |
| Isolation | Low (row-level) | Medium | Maximum |
| Noisy neighbor | High risk | Medium risk | No risk |
| Migration complexity | Simple | Medium (N schemas) | Complex (N databases) |
| Per-tenant backup | Hard | Easy | Easy |
| Data residency | Hard | Hard | Easy (per-region DB) |
| Cross-tenant queries | Easy (same table) | Possible (same server) | Impossible |
| Scaling | Vertical only | Vertical | Per-tenant |
| Provisioning | Instant (new row) | Fast (new schema) | Slow (new database) |
| Best for | Startups | Growth stage | Enterprise |
Modern Approach: Database Branching#
Tools like Neon and Turso make database-per-tenant affordable:
Neon (Serverless Postgres)#
- Branch creation: < 1 second (copy-on-write)
- Scale to zero when tenant is idle (no cost)
- Thousands of databases per project
Turso (Edge SQLite)#
- Per-tenant embedded replicas (in-app, zero latency reads)
- 10K+ databases per account on free tier
- Global replication per tenant
// Turso: create database per tenant
const db = createClient({
url: `libsql://${tenantId}-myapp.turso.io`,
authToken: process.env.TURSO_TOKEN,
});
Row-Level Security (RLS)#
PostgreSQL's built-in tenant isolation:
-- Enable RLS
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- Policy: users can only see their tenant's data
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant_id'));
-- Set tenant context per request
SET app.tenant_id = 'acme';
SELECT * FROM orders; -- Only returns acme's orders
Supabase uses RLS as its primary security model. Every query is automatically filtered.
Application-Level Patterns#
Tenant Resolution#
How to identify the tenant for each request:
1. Subdomain: acme.myapp.com → tenant = "acme"
2. Path: myapp.com/acme/dashboard → tenant = "acme"
3. Header: X-Tenant-ID: acme
4. JWT claim: { "tenant_id": "acme", ... }
5. API key: sk_acme_... → lookup tenant from key
Subdomain is most common for customer-facing SaaS. JWT claims for APIs.
Tenant Context Middleware#
// Express middleware
app.use((req, res, next) => {
const tenant = req.hostname.split(".")[0]; // acme.myapp.com → acme
req.tenantId = tenant;
// Set database context
db.raw(`SET app.tenant_id = '${tenant}'`);
next();
});
Tenant-Aware Caching#
Cache key: tenant:{tenantId}:resource:{resourceId}
Example: tenant:acme:user:123
Never share cache between tenants. Always include tenant_id in cache keys.
Handling the Noisy Neighbor#
One tenant's heavy query shouldn't slow down everyone:
- Rate limiting per tenant — 100 req/min per tenant, not global
- Query timeout — kill queries running > 5 seconds
- Connection pooling — limit connections per tenant
- Resource quotas — CPU/memory limits per tenant (K8s)
- Queue isolation — separate queues for heavy tenants
Architecture Examples#
Startup SaaS (Shared DB)#
Client → API → PostgreSQL (shared, RLS)
→ tenant_id on every table
→ Redis (tenant-prefixed keys)
→ S3 (tenant-prefixed paths)
Growth SaaS (Hybrid)#
Client → API Gateway (tenant from subdomain)
→ Shared services (auth, billing)
→ Tenant router
→ Small tenants → Shared PostgreSQL (RLS)
→ Large tenants → Dedicated PostgreSQL
→ Redis Cluster (tenant-namespaced)
Enterprise SaaS (Full Isolation)#
Client → API Gateway
→ Tenant A → Dedicated K8s namespace
→ Own PostgreSQL, Redis, workers
→ Tenant B → Dedicated K8s namespace
→ Own PostgreSQL, Redis, workers
→ Shared: Auth, billing, monitoring
Migration Path#
Stage 1 (0-100 tenants): Shared DB + RLS
Stage 2 (100-1K tenants): Schema-per-tenant for large customers
Stage 3 (1K+ tenants): Database-per-tenant for enterprise
Don't over-engineer from day one. Start shared, isolate when you need to.
Summary#
- Start shared — row-level isolation with RLS is simple and cheap
- RLS is your friend — PostgreSQL enforces isolation at the database level
- Subdomain routing for tenant resolution
- Always prefix cache/storage keys with tenant_id
- Rate limit per tenant — prevent noisy neighbors
- Modern tools (Neon, Turso) make DB-per-tenant affordable
- Migrate gradually — shared → schema → dedicated as tenants grow
Design multi-tenant architectures at codelit.io.
Try these templates
Scalable SaaS Application
Modern SaaS with microservices, event-driven processing, and multi-tenant architecture.
10 componentsNetflix Video Streaming Architecture
Global video streaming platform with adaptive bitrate, CDN distribution, and recommendation engine.
10 componentsSearch Engine Architecture
Web-scale search with crawling, indexing, ranking, and sub-second query serving.
8 componentsContinue learning
Go deeper on system design
As an Amazon Associate I earn from qualifying purchases. Codelit may receive a commission at no extra cost to you.
Building Microservices
Sam Newman · 2021
600 pages on splitting a monolith: service boundaries, data decomposition, migration order.
Fundamentals of Software Architecture
Mark Richards, Neal Ford · 2025
Scores eight architecture styles against the same characteristics, so trade-offs are side by side.
Obviously Awesome
April Dunford · 2019
A ten-step positioning exercise you can run in an afternoon, with before/after rewrites.
4.6 (2.4k)An Elegant Puzzle
Will Larson · 2019
Worked systems for team sizing, hiring loops, and migrations, drawn from Uber and Stripe.
4.5 (1.4k)