Web Development · Architecture Patterns
Multi-Tenant SaaS: Pooled, Silo, and Bridge
Shared database or one per customer? Most SaaS teams pick wrong for their stage and pay later. How the three patterns trade off, and which one you need.
Abhishek Gupta
5 min read
Sponsored
Every B2B SaaS team hits this decision, usually later than they should have: does each customer get their own database, or do they all share one? Get it wrong in one direction and you’re rebuilding your data layer under a compliance deadline. Get it wrong in the other and you’re running hundreds of near-identical databases for customers who never needed the isolation. The honest answer is that most teams should start pooled, and a specific, nameable trigger, not a vague sense that “enterprise customers will want isolation eventually,” is what should move any given tenant off it.
The three patterns
Pooled: one database, shared schema, tenant_id everywhere. Every tenant’s rows live in the same tables, distinguished by a tenant_id column, with isolation enforced by application logic, database row-level security, or both. This is the default shape for early-stage B2B SaaS: one application instance, one database (or a small number), lowest operational overhead, easiest to back up, easiest to run a schema migration against once instead of hundreds of times.
-- Postgres row-level security: the database enforces tenant
-- isolation even if application code forgets to filter
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON invoices
USING (tenant_id = current_setting('app.current_tenant')::uuid);
-- Every query now implicitly filters by the tenant set for
-- the current connection, whether or not the query author
-- remembered to add WHERE tenant_id = ...
The tradeoff is isolation strength. A row-level security policy that’s misconfigured, or application code that bypasses it with a raw query, turns into a cross-tenant data leak rather than a contained failure. That risk is real, but it’s a code-review and testing problem, not a reason to avoid the pattern outright.
Silo: one database or schema per tenant. Each tenant gets a physically separate database, or at minimum a separate schema within a shared database instance. Isolation is structural rather than logical: there’s no query bug that leaks tenant B’s data into tenant A’s response, because the two are never in the same database to begin with.
The cost is real and it compounds. A schema migration that was one ALTER TABLE in a pooled setup becomes N migrations, one per tenant database, each of which can fail independently. Backups, monitoring, and connection pooling all multiply by tenant count. At a hundred tenants this is manageable with the right tooling. At ten thousand, it’s a distinct operational discipline that most engineering teams aren’t staffed for.
Bridge: pooled by default, silo for the tenants who need it. This is less a third pattern than a routing decision layered on top of the first two. Most tenants, typically your self-serve and mid-market customers, live in the shared pooled database. A smaller set, your largest accounts or the ones with a specific compliance or data-residency requirement, get dedicated databases. The application’s data access layer decides where to route each request based on tenant configuration, not a hardcoded connection string.
What decides it: a real trigger, not a feeling
| Trigger | What it usually means |
|---|---|
| An auditor or contract requires physical data separation | Move that tenant to silo. Row-level security won’t satisfy this requirement regardless of how well it’s implemented. |
| A specific tenant’s query volume is degrading performance for others | Move that tenant to silo, or at minimum a separate connection pool, before it becomes everyone’s incident. |
| ”We think enterprise customers will eventually want isolation” | Not a trigger. Build the pooled system well, with row-level security enforced at the database layer, and design your data access layer so a later migration is a data move, not a rewrite. |
| Data residency law (a customer’s data must stay in a specific region) | Depends on scope. Sometimes solved with database-level regional deployment inside a still-shared architecture; sometimes genuinely requires per-tenant silo. Read the actual regulation before assuming silo is required. |
The pattern worth avoiding is building silo infrastructure speculatively, before any tenant has actually triggered the need for it. It’s expensive to build, expensive to operate, and the requirements you guessed at rarely match the ones a real enterprise contract or auditor eventually asks for.
Designing for a later migration, without building it yet
The cheapest insurance against picking wrong is architectural, not organizational: never let application code assume there’s exactly one database connection. Route every data access through a layer that resolves “which database for this tenant” as an explicit lookup, even when that lookup always returns the same pooled database today. When a tenant eventually needs to move to a dedicated database, that becomes a data migration and a config change instead of a rewrite of every query in the codebase.
This is the same discipline behind database read replica design: decide the routing abstraction early, even before you need more than one target, because retrofitting it under a deadline is far more expensive than building it in from the start. If you’re scoping a multi-tenant architecture for a new product, or deciding whether an existing pooled system needs to start bridging to silo for a specific account, that’s the kind of architecture review our engineering team does before the first enterprise contract forces the decision under pressure.
Frequently asked questions
- Which pattern should a new SaaS product start with?
- Pooled, in almost every case. A single shared database with a tenant_id column and row-level security (or equivalent middleware enforcement) is the cheapest to build, the cheapest to run, and the easiest to reason about when your engineering team is small. Don't build silo isolation for a customer base you don't have yet; you're optimizing for a problem, enterprise compliance requirements and noisy-neighbor risk at scale, that doesn't exist until you have paying enterprise customers asking for it.
- What's the actual risk with a pooled database?
- The risk isn't performance, it's a coding mistake. If every query has to remember to filter by tenant_id, someone eventually forgets, and that's a cross-tenant data leak, not a slow query. Postgres row-level security policies enforced at the database level, not just in application code, close most of that gap: the database rejects a query missing tenant context instead of trusting every code path to get it right.
- When does silo (database-per-tenant) actually make sense?
- When a specific tenant's contract, compliance requirement, or data residency law demands physical data separation, and no amount of row-level security satisfies an auditor. It also makes sense when one tenant's usage pattern is large enough to create real noisy-neighbor risk for everyone else sharing the database. Outside those two triggers, silo is usually solving a problem you don't have at a cost you didn't need to pay.
- What is the bridge pattern in practice?
- Most tenants live in a shared, pooled database, and a small number, usually your largest or most regulated customers, get a dedicated database or schema. The application's data access layer routes each request to the right target based on tenant configuration. This is the shape most production B2B SaaS companies land on once they have both a self-serve tier and enterprise contracts, because it lets you pay the operational cost of isolation only for the tenants who actually need it.
- Can we switch patterns after launch without a rewrite?
- Yes, if your data access layer already treats tenant_id as a routing decision rather than hardcoding a single connection everywhere. Moving one tenant from the shared pool to a dedicated database becomes a data migration and a routing config change instead of an architecture change. The upfront work worth doing early isn't building silo infrastructure, it's making sure nothing in your codebase assumes there's only ever one database.
Sources
Sponsored
More from this category
More from Web Development
Sponsored
Discussion
Join the conversation.
Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.
Sponsored