The multi-tenancy pattern you pick sets infrastructure cost, deploy speed, and what fits inside a compliance contract. Three models dominate SaaS: a tenant_id column in a shared schema, schema-per-tenant, and database-per-tenant. Each buys a level of isolation at a different price.

Shared schema with a tenant_id column

This model wins on density and cost: every row from every tenant lives in the same tables, filtered by tenant_id. One migration updates the entire base at once. Isolation demands row-level security: Postgres RLS policies using current_setting('app.tenant_id') enforce the filter at the engine level, above the application. Noisy-neighbor risk lives in shared indexes and the connection pool, where the giant tenant fights smaller ones for resources.

Schema per tenant

You get stronger logical isolation and feasible per-tenant restores: pg_dump of the affected client's schema spares everyone else. The price arrives with migrations: scripts run N times, and thousands of schemas stretch deploys from minutes to hours. Connection pooling strains past a few hundred tenants, because each schema multiplies catalog entries and prepared statements in the pool.

Database per tenant

Maximum isolation for buyers with compliance demands: data separated at the storage level, and a dedicated-database pricing tier becomes a trivial commercial argument on the Enterprise plan. Infrastructure cost peaks here; managed options (RDS, Neon) cut the operational pain of patching, backup, and failover across hundreds of databases.

The hybrid almost every SaaS ends up building

Enterprise gets a dedicated database, standard shares. A routing layer reads tenancy config from cache (Redis) and points each request at the right connection. The arrangement serves sales without forcing the whole product into exclusive-database costs.

How do you prevent leaks between tenants?

Every query path needs tenant scope. Middleware that extracts the tenant from the JWT or subdomain and sets current_setting beats manual WHERE clauses, which someone forgets on the new endpoint. An automated suite runs on every PR: it attempts cross-tenant access and expects denial. An isolation bug is a security incident waiting for a date; treat every failure of that test as a release blocker.

Migrating between patterns

Start shared and split later, and backfill plus query rewrites eat weeks; start isolated and consolidate, and the bill grows higher still. Decide by buyer: a sales pipeline pulling heavy compliance (health, finance) demands isolation compatible with enterprise contracts from day one.