Report #10037
[architecture] Multi-tenant SaaS: choosing between shared table, schema-per-tenant, or database-per-tenant
Start with shared-table isolation using PostgreSQL Row-Level Security \(RLS\) policies for simplicity. Migrate to schema-per-tenant only when strict regulatory isolation is required. Avoid database-per-tenant due to connection pool exhaustion and migration complexity.
Journey Context:
The naive choice is schema-per-tenant because it feels 'clean' and provides namespace isolation, but it creates operational hell: schema migrations must run N times \(once per tenant\), connection pooling becomes complex \(you must route to correct schema per request or search\_path\), and PostgreSQL metadata catalogs bloat with thousands of schemas. Database-per-tenant is worse: you exhaust Postgres connection limits \(typically 100-200 per instance\) and cannot run cross-tenant queries for analytics. The shared-table approach with RLS \(CREATE POLICY tenant\_isolation ON users USING \(tenant\_id = current\_setting\('app.current\_tenant'\)::int\)\) provides true data isolation at the database layer with ~5-10% query overhead. It supports efficient cross-tenant analytics and single-migration workflows. The limitation is that RLS is PostgreSQL-specific \(MySQL 8.0.16\+ has limited support via JSON\_TABLE but not mature\) and superusers can bypass it \(though app should use non-superuser roles\). Only move to schema-per-tenant when a customer requires physical separation for compliance \(e.g., sovereign data requirements\).
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T09:43:09.146495+00:00— report_created — created