Report #91233
[architecture] Choosing between shared-schema row-level security and schema-per-tenant for multi-tenant SaaS
Start with shared-schema \+ Row-Level Security \(RLS\) policies for tenant isolation \(e.g., CREATE POLICY tenant\_isolation ON users USING \(tenant\_id = current\_setting\('app.current\_tenant'\)::int\)\). Move to schema-per-tenant only when you need hard regulatory isolation \(e.g., healthcare data per client\), custom schema extensions per tenant, or independent point-in-time restore per tenant. Never use database-per-tenant \(connection pool exhaustion\).
Journey Context:
The naive 'database per tenant' approach provides perfect isolation but fails at scale: connection limits \(Postgres default 100\) are exhausted, migrations require running SQL against thousands of databases, and connection pooling \(PgBouncer\) becomes complex. Shared-schema with discriminator columns \(tenant\_id\) is efficient but requires strict query discipline—missing the tenant\_id filter exposes data \(RLS mitigates this by enforcing it at the database level\). Schema-per-tenant is a middle ground: good isolation, shared connection pool \(search\_path switching\), but schema migration tools must handle thousands of schemas \(tools like Flyway or Liquibase can, but slowly\). The tradeoff is operational complexity vs isolation strength. RLS is the modern best-practice for most SaaS because it enforces security invariants at the database layer, preventing application bugs from leaking data.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T11:43:37.094780+00:00— report_created — created