Multi-tenancy is the architectural decision that follows every SaaS product into its grave if you get it wrong. We've migrated enough projects between models to have strong opinions. Here's the framework we use.

The three models

You have three real options for tenant isolation:

  1. Shared schema, shared database. All tenants in the same tables, with a tenant_id column on every row.
  2. Schema-per-tenant, shared database. Each tenant has its own Postgres schema; the application picks the right schema per request.
  3. Database-per-tenant. Each tenant has its own physical database (or logical database on a shared cluster).

There's no "best" choice. There's the right choice for your customer profile, compliance requirements, and operational maturity.

Shared schema: the default

For most B2B SaaS, shared schema is the right starting point. Every table has a tenant_id column. Every query filters on it. Row-Level Security policies enforce isolation at the database level so a bug in application code can't leak data across tenants.

Strengths:

  • Operational simplicity. One schema to migrate, one database to back up, one query plan to optimise.
  • Cheap. You can run thousands of tenants on a single Postgres instance.
  • Cross-tenant analytics are trivial. (For your own internal use, never customer data.)

Weaknesses:

  • A bug in your tenant filtering is catastrophic.
  • One huge tenant can hot-spot your shared tables and degrade everyone's performance.
  • Per-tenant customisation (custom fields, custom workflows) gets ugly fast.
  • Some enterprise customers will refuse it. "Our data shares tables with our competitors" is a hard sell.

Schema-per-tenant: the middle ground

Each tenant gets a separate Postgres schema. The connection pool sets search_path to the tenant's schema at the start of each request. All queries automatically target the right tenant's tables.

Strengths:

  • Stronger isolation than shared schema. A query bug can't accidentally return another tenant's data.
  • Per-tenant customisation is easier. Add a column to one tenant's schema without touching others.
  • Per-tenant backups and migrations are feasible.

Weaknesses:

  • Schema migrations multiply. If you have 1,000 tenants, every migration runs 1,000 times.
  • The Postgres catalog grows. Past ~10,000 schemas, performance degrades noticeably.
  • Connection pooling gets complicated. PgBouncer in transaction mode struggles with per-request search_path.

This is the model that sounds appealing but in practice is the worst of both worlds for most products. We use it sparingly.

Database-per-tenant: enterprise mode

Each customer gets their own database. Either physically separated (different RDS instance) or logically separated (separate CREATE DATABASE in the same cluster).

Strengths:

  • Maximum isolation. Resource limits, backup schedules, even Postgres versions can vary per tenant.
  • Enterprise customers love it. "Our data is in our own database" is an easy security conversation.
  • Compliance (HIPAA, SOC 2, data residency) is dramatically simpler.
  • You can offer customers their own VPC, their own region, their own everything.

Weaknesses:

  • Expensive. Hundreds or thousands of databases is significant operational and compute cost.
  • Cross-tenant queries are impossible without a separate analytics pipeline.
  • Provisioning a new tenant is a multi-second operation, not an INSERT.

The hybrid model

What we usually ship for serious B2B SaaS is a hybrid:

  • Small tenants share a database with shared-schema isolation. Cheap, simple, sufficient.
  • Large enterprise tenants get their own database. They pay for it. They get the isolation guarantees they want.
  • A tenant routing layer at the application boundary figures out which database to point a given request at.

This lets you serve 10,000 small customers on a single shared instance and a handful of large customers on dedicated instances. The operational cost matches the customer value.

The other things that matter

Tenant isolation isn't just database design. Other concerns:

  • Auth and identity. Tenant-scoped users, cross-tenant users, and identity providers per tenant (SAML, SSO).
  • Background jobs. Make sure your worker fleet routes jobs to the right tenant's database.
  • Caching. Cache keys need tenant prefixes. The number of "we leaked data via Redis" incidents we've seen is depressing.
  • Observability. Logs and metrics need to be tagged with tenant ID so you can debug per-tenant problems.
  • Cost attribution. Be able to answer "how much does tenant X cost us to serve?" — both for pricing and for support triage.
Pick the simplest model that meets your isolation requirements today. Plan for the hybrid you'll need in three years.

What we'd pick today

For a greenfield B2B SaaS in 2026, our default is shared schema + Row-Level Security in Postgres, with a clear plan to lift the largest customers into dedicated databases when they ask for it. That gives the simplest possible operational story for the early years, with a graceful upgrade path when enterprise customers start showing up. The mistake is over-engineering on day one — and the related mistake is leaving the upgrade path so vague that the migration becomes terrifying when you finally need it.