SaaSMay 26, 2026 · 9 min

Multi-tenant SaaS: RLS vs schema-per-tenant vs database-per-tenant

There are three common ways to isolate tenant data in a SaaS — a shared schema with row-level security, a schema per tenant, or a database per tenant. Each trades isolation against operational cost. Here's how they actually differ and how I pick.

The decision that's expensive to change

Tenancy is one of the few architecture choices that's brutal to reverse once you have customers. Get it right early. The question is always the same: how strongly must tenants be isolated, and how much operational complexity can you afford to get that isolation?

Option 1 — Shared schema + Row-Level Security (RLS)

All tenants share the same tables; every row carries a tenant_id, and the database enforces, via RLS policies, that a query only ever sees its own tenant's rows. This is my default for most SaaS.

  • Pros: cheapest to operate, one schema to migrate, easy cross-tenant analytics, scales to many tenants.
  • Cons: isolation depends on correct policies + session context; a policy bug is a cross-tenant leak; noisy-neighbor effects at the row level.
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON invoices
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

With RLS, set the tenant context once per request (a single SET LOCAL) and let the database enforce the rest. Never reconstruct tenant scope ad hoc in application queries — that's where leaks come from.

Option 2 — Schema per tenant

Each tenant gets its own Postgres schema (same database). Stronger logical isolation; queries target tenant_x.invoices.

  • Pros: stronger isolation than shared rows; easy per-tenant export; still one database to run.
  • Cons: migrations now run N times (one per schema); thousands of schemas strain tooling; cross-tenant queries get awkward.

Option 3 — Database per tenant

Each tenant gets a dedicated database (or instance). The strongest isolation — and the most operations.

  • Pros: hard isolation, per-tenant backups/restore, easy data residency, no noisy neighbors.
  • Cons: heaviest ops (provisioning, migrations, connection management across many DBs); expensive at scale; cross-tenant analytics is a separate pipeline.

How I choose

Start with shared schema + RLS unless a requirement forces otherwise. Move to schema- or database-per-tenant when you have hard compliance/data-residency needs, a small number of high-value enterprise tenants, or contractual isolation guarantees. Most products never need to leave RLS — and the ones that do usually only move their biggest tenants.

Key takeaways

  • Tenancy is expensive to change — decide deliberately before you have customers.
  • Shared schema + RLS is the right default: cheapest ops, strong-enough isolation if policies are correct.
  • Escalate to schema/DB-per-tenant only for compliance, residency, or a few enterprise tenants.
  • Whatever you pick, enforce isolation in the data layer — never the client.

FAQ

Is RLS safe enough for production SaaS?

Yes, when policies are correct and tenant context is set once per request at the connection level. The risk is human error in policies, which you mitigate with tests that assert cross-tenant queries return nothing.

Can I mix approaches?

Common in practice: shared schema + RLS for most tenants, dedicated databases for a few enterprise customers who require it. Design the app so tenant resolution is pluggable.

AA
Ali Asghar

Senior software engineer & technical lead — 6+ years shipping production multi-tenant SaaS, payments and AI integration in Next.js, Node & TypeScript.

Keep reading
Payment webhooks the hard parts: idempotency, retries and reliabilityHow I built a real-time, cookieless analytics console in Next.js