2026-08-05
Three viable models
Row-level security with a tenant_id on every row is the default we recommend: one database, cheap onboarding, and safety enforced in the database rather than in application code. Schema-per-tenant buys stronger isolation at the cost of migration pain. Database-per-tenant is for compliance-heavy cases where the isolation itself is the product.
RLS done right
The pattern: every table carries tenant_id, a helper function resolves the current tenant from the JWT, and policies enforce that writes can only touch your tenant. The critical discipline is that no code path bypasses the policies — service-role access is restricted to background jobs, audited and logged.
Test RLS by writing a test that tries to cross tenants. If your suite has no negative tenant test, your isolation is a hope, not a guarantee.
Don't forget noisy neighbors
One large tenant can degrade the shared pool for everyone. Add per-tenant rate limits at the API layer, monitor query-time percentiles per tenant, and design your biggest tables with partitioning in mind from the start — retrofitting partitioning on a live multi-tenant table is a weekend you won't enjoy.