Skip to main content

Multi-Tenancy Isolation Models

Level 8: Level 8: Security, Privacy & Multi-tenancyhard30 minmulti-tenancyisolationrls

Choose silo/pool/bridge per the cost-versus-isolation tradeoff (tier it: pool SMB, silo regulated); resolve tenant context from a trusted source on every request and propagate tenant_id everywhere; enforce at the data layer with Postgres RLS or per-tenant keys/routing; add per-tenant quotas for noisy neighbors; and hunt the non-obvious leaks in caches, search indexes, async jobs, ids, and logs.

Multi-tenancy: share infra cheaply, isolate data absolutely

Multi-tenancy is running many customers (tenants) on one platform. The whole game is making it economically cheap to share infrastructure while guaranteeing tenant A can never see tenant B's data. The core spectrum is silo vs pool vs bridge.

Silo gives each tenant dedicated infrastructure: their own database, sometimes their own cluster or even their own cloud account. Strongest isolation, easiest compliance story ("your data is in your own database"), simplest blast radius, but expensive and operationally heavy (you now patch and migrate N databases). Pool shares everything: one database, one schema, rows from all tenants in the same tables distinguished by a tenant_id column. Cheapest and most scalable, but isolation now depends entirely on your code and query discipline, one missing WHERE tenant_id = ? leaks everyone. Bridge is the middle: shared database, separate schema (or separate table set) per tenant. More isolation than pool, cheaper than silo, but schema-per-tenant stops scaling past a few thousand tenants (migrations across 5,000 schemas hurt).

  SILO   dedicated DB/cluster per tenant   strongest isolation, highest cost
  BRIDGE shared DB, schema-per-tenant       middle ground
  POOL   shared schema, tenant_id column    cheapest, isolation is code-enforced

The senior move is tiered isolation: pool your thousands of small self-serve SMB customers for cost efficiency, and silo your regulated enterprise customers (health, finance, government, data-residency requirements) into dedicated databases or accounts. One product, two isolation postures, sold as a premium tier.

Check yourself

Silo, bridge, or pool? Match each statement to the tenancy model it describes.

Every tenant's rows share one table, told apart by a tenant_id column
Each tenant gets a dedicated database, sometimes a whole cloud account
One shared database with a separate schema per tenant
Cheapest to run, and one missing WHERE clause leaks everyone
The premium tier you sell to regulated enterprise customers
Migrations start to hurt at a few thousand tenants

Enforce at the data layer, resolve context early

Wherever tenants share, isolation must be enforced at the data layer, not just the app layer, because app-layer checks are one forgotten WHERE clause from a breach. Postgres Row-Level Security (RLS) is the workhorse: you set current_setting('app.tenant_id') at the start of each request's transaction, and a policy USING (tenant_id = current_setting('app.tenant_id')::uuid) makes the database itself refuse to return other tenants' rows even if application SQL forgets the filter. Combine with per-tenant encryption keys (crypto-isolation) and connection/schema routing where the tenant maps to a database.

Interview nuance: the deciding detail is where and when tenant context is resolved. It must be established on every request, before any business logic runs, from a trusted source: the JWT/session claim or a subdomain (acme.app.com), never from a request body field the client can set. Then tenant_id propagates through the entire call chain (into the DB session var, into cache keys, into async job payloads, into log fields). If tenant context is derived late or from untrusted input, everything downstream is exploitable.

The non-obvious leakage vectors

The part that separates a strong answer is where real multi-tenant breaches happen even when the primary DB path is perfect:

Check yourself
Your database isolation is airtight: RLS on every table, verified. The app also caches rendered profiles under the key 'user:profile:42'. Tenant A requests profile 42. Where is the leak?
  • Caches: a cache key of user:profile:42 with no tenant prefix serves tenant B's cached object to tenant A. Every cache key must include tenant_id.
  • Search indexes: Elasticsearch/OpenSearch queries need a tenant filter (or per-tenant index); a global search that forgets it returns everyone's documents.
  • Background jobs / async workers: a job dequeued without its tenant context runs with ambient or wrong tenant, and RLS silently returns nothing or the wrong rows. Carry tenant_id in the job payload and re-establish context on pickup.
  • Shared/sequential IDs: guessable global ids invite IDOR across tenants.
  • Log and metrics aggregation: dumping raw payloads into a shared logging pipeline can expose tenant B's PII to tenant A's support view.

Finally, shared infra creates the noisy neighbor problem: one tenant's traffic spike starves everyone. Enforce per-tenant quotas and rate limits so tenants get fair-share isolation of capacity, not just data.

Recap: choose silo/pool/bridge per the cost-versus-isolation tradeoff (tier it: pool SMB, silo regulated); resolve tenant context from a trusted source on every request and propagate tenant_id everywhere; enforce at the data layer with Postgres RLS or per-tenant keys/routing; add per-tenant quotas for noisy neighbors; and hunt the non-obvious leaks in caches, search indexes, async jobs, ids, and logs.

Check yourself
Final design decision: where does the tenant id come from on each request, and what enforces the isolation?

Apply

Your turn

The task this lesson builds to.

Design tenant isolation for a B2B SaaS spanning small self-serve customers and large regulated enterprise customers on one platform.

Think about

  1. What is the silo vs pool vs bridge tradeoff?
  2. Where must tenant context be resolved and enforced?
  3. What are the non-obvious cross-tenant leakage vectors?

Practice

Make it stick

A second problem on the same idea, so it survives past today.

Design the multi-tenant data isolation for a Slack-scale workspace platform: millions of workspaces from 3-person startups to 250,000-seat enterprises, with enterprise customers demanding data residency (EU/US), their own encryption keys (BYOK), and a hard audit guarantee that no other workspace's data is ever co-mingled in a way they can access.

Think about

  1. How does sharding by workspace double as an isolation strategy?
  2. Why must tenant/region context be resolved before any I/O?
  3. How does BYOK turn the isolation guarantee into a cryptographic fact?