Multi-tenant architecture: the decision you cannot cheaply reverse
Three models, three sets of trade-offs, and one that is right for almost everybody at the start.
Most SaaS products should start with a shared schema and a tenant id enforced by Postgres row-level security. It is the cheapest to build and migrate. Move to schema-per-tenant or database-per-tenant only when a specific customer contract or regulation requires physical isolation.
Key takeaways
- Add the tenant boundary before your first customer, even if you only have one.
- Row-level security enforces isolation in the database, not in application code you might forget.
- Schema-per-tenant makes migrations a batch job; database-per-tenant makes them a project.
- Enterprise buyers ask about isolation — have a written answer before the question arrives.
Tenancy is the decision most often deferred and most expensive to reverse. Every query, every permission check and every migration inherits it.
The three models
| Model | Isolation | Migration cost | Fits |
|---|---|---|---|
| Shared schema + tenant id | Logical, enforced by RLS | One migration for everyone | Almost every product at the start |
| Schema per tenant | Strong, same database | One migration per schema, batched | Hundreds of tenants with compliance needs |
| Database per tenant | Physical | A migration programme | Enterprise, regulated, per-customer residency |
Why shared schema wins early
One schema, one migration, one connection pool, one backup. With Postgres row-level security, isolation is enforced by the database itself: a query that forgets its tenant filter returns nothing rather than returning someone else's data. That is a materially different guarantee from remembering to write `WHERE tenant_id = ?` in three hundred places.
What it requires you to get right
- Every table with tenant-owned data carries a tenant column, with a policy on it — no exceptions for 'small' tables.
- The tenant context is set per request from the session, never passed in from the client.
- Background jobs and admin tooling set the context explicitly rather than bypassing RLS by habit.
- Tests include a cross-tenant access attempt for every new table. Once, then automatically, forever.
When to move up a level
Move when a contract requires it and the revenue justifies it — not in anticipation. A hybrid is common and perfectly respectable: shared schema for the self-serve base, a dedicated database for the three enterprise customers who pay for it.