Single vs Multi-Tenant SaaS Architecture
Tenancy is one of the three decisions you can't cheaply reverse — we made that case in choosing a SaaS tech stack. This is the full version: what the options are, what genuinely forces the expensive ones, and the part most discussions skip.
The three models
Shared schema. One database, one set of tables, a tenant_id column on every row. Every query filters by it. Cheapest to run, simplest to operate, and the default for most B2B SaaS.
Schema per tenant. One database, but each customer gets their own set of tables. Stronger separation, and you can back up or restore one customer without touching others. The operational cost climbs with tenant count — migrations now run N times.
Database per tenant. Complete separation. Strongest isolation, easiest compliance story, and by far the most expensive to operate. Every schema change is a fleet operation.
| Shared schema | Schema per tenant | Database per tenant | |
|---|---|---|---|
| Cost per tenant | Lowest | Medium | Highest |
| Isolation | Logical only | Strong | Complete |
| Migrations | One | N | N |
| Restore one customer | Hard | Straightforward | Trivial |
| Noisy neighbour risk | Real | Reduced | None |
| Practical tenant ceiling | Very high | Hundreds | Tens to low hundreds |
The part that actually matters
Here's what most comparisons miss. Your isolation model is only as strong as its enforcement, and in the shared-schema model enforcement is where teams get hurt.
A tenant_id column protects nothing on its own. It protects you when every single query filters on it — and "every single query, forever, including the one written at 6pm on a Friday by someone new" is not a property you get from discipline. It's a property you get from structure.
Make it structural, not remembered:
- Row-level security in the database, so the data layer itself refuses to return another tenant's rows even if application code forgets. This is the strongest single control available in the shared model.
- One access layer that always applies the tenant scope, with raw queries banned outside it.
- A test that tries to read another tenant's data and fails the build if it succeeds. Cheap, and it catches the regression that matters most.
Without something like this, shared-schema tenancy is one forgotten WHERE clause away from leaking data between customers — the single most damaging bug a SaaS can ship.
What genuinely forces heavier isolation
Not scale. Not preference. These:
- A contract that requires it. An enterprise customer whose security review mandates a dedicated database. This is the most common real reason, and it's a sales question, not an engineering one.
- Data residency. Customers whose data must stay in a specific country. You need per-region separation regardless of what you'd otherwise choose.
- Per-tenant restore. If "restore this one customer to yesterday" is a real requirement, shared schema makes it genuinely painful.
- Wildly uneven tenants. One customer with a hundred times the data of everyone else will degrade shared infrastructure for the rest.
- Regulatory regimes that expect demonstrable physical separation.
If none of these apply, shared schema is almost certainly right, and it scales further than founders expect.
The hybrid nobody mentions
You don't have to pick one for everybody. Pool most tenants in shared schema, and isolate the few who require it.
This is what mature B2B SaaS usually converges on: the long tail of self-serve customers shares infrastructure, while a handful of enterprise accounts get dedicated databases because their contracts say so.
It costs more in operational complexity — two paths to maintain — but it means you don't price every customer as though they were the most demanding one. Design for it early even if you don't use it: keep tenant resolution in one place, so pointing a tenant at a different database later is a configuration change rather than a refactor.
Which direction is easier to move
Worth knowing before you choose:
- Shared → isolated is doable. Extracting one tenant into its own database is a well-understood migration. Unpleasant, not architectural.
- Isolated → shared is painful. Merging separate databases means reconciling ID collisions and schema drift that accumulated while each ran independently.
So when genuinely unsure, start shared. The cheaper option is also the more reversible one, which is unusual and worth taking advantage of.
The honest default
For most B2B SaaS: shared schema, row-level security enforced in the database, tenant scope applied in one access layer, and a test that proves isolation holds. Add per-tenant databases when a contract or a regulator requires it, not because it feels safer.
The isolation model is a smaller decision than how rigorously you enforce whichever one you pick. A shared schema with database-enforced row-level security is safer in practice than a database-per-tenant setup where the routing logic can pick the wrong one.
If you'd like a second opinion on tenancy before it's baked into every query you'll ever write, that's exactly the kind of decision worth a conversation. See how we approach SaaS development, or get in touch.
Ready to build it? Let's talk about your project.
SaaS & Mobile Apps →