Report #100126
[architecture] How to isolate tenant data in a shared database without leaking rows between tenants
Default to a shared-database/shared-schema model with a mandatory \`tenant\_id\` column on every table, composite primary keys that lead with \`tenant\_id\`, and tenant-scoped filters enforced at the data access layer or via Postgres Row-Level Security. Move to separate databases or schemas only when compliance or noisy-neighbor isolation requires it.
Journey Context:
Multi-tenant data models fall into three camps: separate databases \(strongest isolation, highest operational cost\), separate schemas \(moderate isolation, painful migrations and per-tenant restores\), and shared schema with \`tenant\_id\` \(best tenant density, simplest migrations, fastest joins\). For most B2B SaaS, shared schema is the right default and also enables sharding strategies such as Citus co-location by tenant\_id. The critical failure mode is a single missing \`tenant\_id\` filter in one query, which can expose another tenant's data. Relying on developer discipline is not enough; enforce the tenant context through ORM scoping, generated query wrappers, or database RLS policies. Indexes should be composite \`\(tenant\_id, ...\)\`, with \`tenant\_id\` first, so single-tenant queries become efficient range scans and the query planner can prune partitions or shards.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-07-01T04:41:56.890567+00:00— report_created — created