Multi-tenant isolation: row-level security or separate databases?
The hardest decision in any SaaS platform to reverse, argued from both sides — with the cost and compliance numbers that usually settle it.
The decision
Every multi-tenant platform has to answer one question early: does every customer's data sit in the same tables, separated logically, or does each customer get their own database?
It is worth taking seriously because it is the decision you will least enjoy revisiting. Changing it later means a data migration that touches every table, every query and every backup procedure, usually while the platform is live and growing.
Shared tables with row-level security
One database, one set of tables, a tenant identifier on every row, and the database itself enforcing that a session can only see rows belonging to its tenant. PostgreSQL row-level security makes this genuinely robust — the policy sits below the application, so an application bug cannot leak across tenants the way a forgotten WHERE clause would.
It is substantially cheaper to operate. One instance to size, patch, back up and monitor. Schema migrations run once. Onboarding a new customer is an INSERT. Most platforms serving small and medium businesses should start here and would be wrong not to.
- Lowest infrastructure cost per tenant by a wide margin
- One migration, applied once, for all customers
- Instant tenant provisioning
- Cross-tenant analytics are a normal query rather than a pipeline
A database per tenant
Each customer gets their own database, sometimes their own instance. Isolation is physical rather than policy-based, which is a materially different thing to put in front of an enterprise security review.
The costs are real and ongoing. Migrations must run across every database, with partial-failure handling. Connection pooling becomes a genuine engineering problem past a few hundred tenants. Any question that spans customers needs a separate analytics pipeline. Provisioning takes minutes rather than milliseconds.
Nobody chooses database-per-tenant because it is elegant. They choose it because a customer's procurement team asked where exactly their data lives, and 'in the same table as your competitors' ended the conversation.
What usually settles it
In practice the decision is made by three things, and none of them is technical preference.
The first is your customer size. If your median contract is a few hundred pounds a month, per-tenant infrastructure costs will exceed the margin. If your median contract is six figures and comes with a security questionnaire, isolation is cheaper than losing the deal.
The second is regulatory. Data residency requirements — this customer's data must remain in this jurisdiction — are far simpler to satisfy when a customer maps to a database you can place deliberately.
The third is your largest tenant. One customer generating a hundred times the volume of the median will create noisy-neighbour problems in a shared model long before anything else breaks.
The pragmatic middle
The pattern we most often recommend is shared tables with row-level security as the default, plus the ability to move an individual tenant onto a dedicated database without a rewrite. That requires one architectural discipline from day one: all data access goes through a tenant-resolution layer that knows which connection a tenant uses.
Build that layer at the start and it is nearly free. Retrofit it at four hundred tenants and it is a quarter of engineering time. It is the cheapest insurance policy in SaaS architecture.
Tell us what you are building
A short call, an honest read on whether we are the right fit, and a clear next step either way. No pitch deck.
Replies within one working day
