If you're building a B2B SaaS, you're building multi-tenant—even if you don't call it that yet. The question is which flavor.
The three patterns
Pooled (shared everything)
One database, one schema, every row has a tenant_id. Cheap, simple, scales horizontally well. The risk is the 'noisy neighbor'—one tenant's heavy query degrades everyone—and the operational risk that a bug in WHERE clauses leaks data.
Siloed (database per tenant)
Each tenant gets their own database. Strong isolation. Easy compliance story. Painful to operate: migrations have to run N times, monitoring multiplies, costs creep.
Hybrid (pooled compute, siloed data)
Shared application tier, separate database (or schema) per tenant. The pragmatic middle. You keep most of the operational simplicity of pooled, get most of the isolation of siloed.
What actually drives the choice
- Compliance ceiling: HIPAA or regulated finance? Lean siloed.
- Tenant size distribution: a few whales, many minnows? Hybrid lets whales graduate to their own DB.
- Customization needs: per-tenant schemas? Hybrid or siloed.
- Cost sensitivity: a fleet of tiny tenants is cheapest pooled.
The Postgres-specific tricks worth knowing
Row-level security (RLS) makes pooled safer—database enforces tenant isolation, not application code. Set a session variable for tenant_id, define RLS policies once, and the database refuses to leak even if your code has a bug.
Schema-per-tenant in a single Postgres works at the low hundreds. Past that, search_path manipulation and connection pooling get gnarly. Plan to graduate to database-per-tenant for your top customers.
Scale ceilings, roughly
- Pooled with RLS: comfortable to ~50K tenants on a single Aurora cluster.
- Schema-per-tenant: comfortable to ~500 schemas before management overhead bites.
- Database-per-tenant: comfortable to ~50–100 instances per region without dedicated tooling.