Multi-Tenant SaaS Architecture: Pool, Bridge, or Silo?
Most B2B SaaS founders agonize over multi-tenant architecture and pick wrong on day one. Here is the honest comparison of pool, bridge, and silo — and why most companies stay in pool forever, with code-level patterns and a real migration path.
On this page
Every B2B SaaS founder eventually stares at the same architecture diagram and wonders: do I need separate databases per customer? Most answers I see online are either 'yes, for security' (wrong by default) or 'no, just use tenant_id' (incomplete). The truth is more interesting and depends on three factors: compliance, noisy-neighbor risk, and the realistic cost of wrong.
After designing or auditing multi-tenant architectures for dozens of SaaS — from two-person seed startups to mid-stage enterprise platforms — the same three patterns show up over and over. They have established names (pool, bridge, silo) and well-understood trade-offs. This guide is the honest comparison: when to pick each, how to implement it on Postgres, and what the migration path looks like when you eventually need to evolve.
The three patterns, explained
Pool, bridge, and silo describe how you physically separate data between tenants. The names come from AWS's SaaS reference architecture but every cloud and database vendor uses similar terminology. Pick one as your default and treat moving between them as a migration project, not a config flag.
Pool: one database, tenant_id everywhere
All tenants share the same database, the same schema, and the same tables. Every row has a tenant_id column. Application code (or, better, Postgres Row Level Security) enforces that queries only return rows for the current tenant. This is what Linear, Notion (mostly), Plausible, Cal.com, and a long list of YC B2B SaaS run on.
Bridge: shared database, schema-per-tenant
One Postgres database, but each tenant gets its own schema (tenant_acme.users, tenant_globex.users). Queries are routed by setting search_path at the start of each request. You get logical isolation, the ability to customize per-tenant migrations, and easier per-tenant backups. Cost: significant operational complexity around migrations and connection pooling.
Silo: database-per-tenant
Each tenant gets a fully separate database, often on a separate compute instance. Strongest isolation. Independent scaling. Per-tenant backup and restore. Compliance officers love it. Cost: every database is its own ops burden, and a 1,000-tenant SaaS becomes a 1,000-database SaaS — usually unsustainable below mid-five-figure ACVs.
The honest comparison
The trade-off table. Read this once, then read it again next time someone says 'shouldn't every customer have their own database for security?'
| Dimension | Pool (shared) | Bridge (schema/tenant) | Silo (DB/tenant) |
|---|---|---|---|
| Infrastructure cost | Lowest | Low to medium | High (scales with tenant count) |
| Data isolation | Logical (RLS) | Logical (schema) | Physical |
| Noisy-neighbor risk | High without throttling | Medium | Low |
| Per-tenant customization | Hard | Easier (schema per tenant) | Easy (full DB per tenant) |
| Backup / restore per tenant | Custom export logic | pg_dump --schema | Native pg_dump |
| Migration complexity | Single migration | Run per schema | Run per database |
| Compliance perception | Hardest sell to enterprise | Middle ground | Easiest to defend |
| Right for | Most B2B SaaS, all SMB SaaS | Custom-schema SaaS, mid-market enterprise | Regulated industries, large ACV enterprise |
| Real-world example | Linear, Plausible, Cal.com | Notion (workspace-level), Salesforce orgs | Snowflake, banking SaaS |
Implementing pool with Postgres Row Level Security
Pool done badly is dangerous — one missing tenant_id filter and customer A sees customer B's data. Pool done well is one of the safest patterns in production, because Postgres enforces isolation at the database layer, not the application layer.
- Add tenant_id (UUID or bigint) to every tenant-scoped table. Make it NOT NULL. Index it leading the index for any query that filters by tenant.
- Enable RLS on every tenant table: ALTER TABLE users ENABLE ROW LEVEL SECURITY.
- Create a policy: CREATE POLICY tenant_isolation ON users USING (tenant_id = current_setting('app.current_tenant')::uuid).
- In your application middleware, on every request, run SET LOCAL app.current_tenant = '<tenant uuid>' inside the same transaction as your queries.
- Force the policy on superusers too: ALTER TABLE users FORCE ROW LEVEL SECURITY. Without this, your migration runner bypasses RLS and could leak rows across tenants in a buggy migration.
- Add a database-level integration test: connect, set the tenant variable to tenant A, attempt to read tenant B's data, assert empty result. Run on every CI build.
The composite-index pattern matters. For a table with 50M rows across 5,000 tenants, queries like 'SELECT * FROM events WHERE tenant_id = ? AND user_id = ? ORDER BY created_at DESC LIMIT 50' must have an index on (tenant_id, user_id, created_at DESC). Tenant_id leads. Always. Without this, the query planner scans across tenants and your P99 collapses.
When pool stops being enough
Pool scales further than founders expect — Linear runs millions of users on a small number of pooled Postgres instances. But there are signals that you have outgrown it for a specific subset of your tenant base.
- Noisy neighbors. Your largest enterprise tenant runs nightly batch exports that drive your shared database CPU above 80 percent and degrade everyone else.
- Compliance asks. A prospect's security questionnaire asks 'is our data in an isolated database' and the deal is worth more than the migration cost.
- Per-tenant customization. One tenant needs a custom Postgres extension or a different schema version, and feature-flagging it in pool would create operational chaos.
- Scale of a single tenant. One tenant's data exceeds what you want in a shared database — typically over 500 GB of single-tenant data or over 10K queries per second from one tenant.
- Regulatory residency. A tenant's data must reside in a specific geography, separate from other tenants.
When any of these fire, the answer is rarely 'migrate everyone to silo.' It is usually 'migrate this one tenant to silo, keep the long tail in pool.' This hybrid is the dominant pattern at growth-stage B2B SaaS.
Bridge: the underused middle ground
Bridge — schema per tenant in a shared database — is the pattern most teams skip and a few teams swear by. It gives you logical isolation, easier per-tenant backups, and the ability to migrate schemas independently. The cost is real: connection pooling becomes harder (each tenant's queries should ideally pool separately), migrations now run N times instead of once, and tools like pgBouncer in transaction mode interact awkwardly with search_path.
Bridge is the right pick when you have 10-200 tenants who each need slightly different schemas (custom fields per workspace, for instance), and you do not want either the cost of silo or the complexity of building dynamic schema in pool. Notion's workspace model leans bridge-ish. Salesforce's org architecture is conceptually bridge with heavy customization.
Silo: when separation is the product
Silo is the right call when isolation is part of what you are selling. Banking SaaS, healthcare platforms with PHI, defense contractors, and any product where 'your data is in a database that only you touch' is a sales argument worth real money.
The cost: every tenant is now a deployment. Migrations run N times. Connection limits multiply (Postgres has a hard ceiling around a few thousand connections per instance, which means many silos forces many instances). Operational tooling — backups, monitoring, upgrades — must be automated end-to-end before silo becomes sustainable.
If you are doing silo, invest early in: an automated tenant provisioning pipeline (Terraform or Pulumi creating a new database, applying schema, registering the tenant), automated migration orchestration (something like pgroll fanned out across all tenant databases), and centralized observability per tenant. Without this tooling, silo eats your team alive at 50+ tenants.
Migration paths between patterns
You will probably move between patterns at some point. The good news: the migrations are well-understood and follow the zero-downtime database migration playbook closely.
Pool to silo (one tenant at a time)
Provision a new tenant database. Replicate the tenant's rows from the pool database to the new database (using logical replication or a one-time copy). Dual-write for a short window. Swap reads. Stop writing to pool. Delete the tenant's rows from pool. Two to four weeks per tenant for careful rollouts.
Pool to bridge
Create per-tenant schemas in the same database. Move each tenant's rows from public.* to tenant_X.*. Application code now sets search_path at request time. This migration is mostly a data move; the surface area in code is small if RLS was already in place.
Bridge to silo
Easier than pool to silo because per-tenant data is already isolated. pg_dump --schema=tenant_X, restore into new database, swap. Often a half-day per tenant if your tooling is mature.
Anti-patterns I see in audits
Mistakes that show up across multi-tenant architectures I review.
- Pool without RLS, relying on application-level filters. One missing WHERE clause and you have a data leak. Always defend at the database layer.
- tenant_id not leading the indexes. Composite indexes must lead with tenant_id or your query planner will not benefit from tenant scoping.
- Silo from day one with no automation. Three tenants in, three databases to manage by hand, every migration is a Friday-night incident.
- No per-tenant rate limiting in pool. One tenant runs a runaway script and degrades the database for everyone. Pair this with the rate-limiting patterns guide for fair-share enforcement.
- Bridge with shared connection pool in transaction mode. search_path changes get lost between transactions. Use session mode or per-tenant pools.
- Treating tenant_id as a string when it could be UUID or bigint. Strings burn cache space and slow joins.
- No data export tool per tenant. When a customer leaves and asks for their data, hand-rolling SQL is painful. Build the export pipeline early.
Picking your default and moving forward
If you are starting a B2B SaaS today and you are not in a regulated industry, default to pool with RLS on Postgres. Index tenant_id leading on every composite index. Build the per-tenant rate limit and noisy-neighbor protection before your tenth paying customer. Plan migration paths to silo for the day a six-figure enterprise tenant asks.
If you are in healthcare, finance, or defense from day one, the calculus shifts. Compliance perception is part of your sales motion, and silo gives you the answer enterprise buyers want to hear. Invest in tenant provisioning automation early — that is the only way silo scales.
Either way, this decision compounds. It is one of the three architectural calls — alongside caching strategy and how you run zero-downtime database migrations — that will shape your engineering velocity for years. Get it right, or know exactly when to evolve it.
Frequently asked questions
Should I start with pool, bridge, or silo?
Start with pool unless you have a specific reason not to. Pool is one shared database with a tenant_id column on every row, enforced by Postgres Row Level Security. It is the cheapest, simplest, and the pattern almost every B2B SaaS rides until at least mid-eight figures of ARR. Bridge and silo are upgrades you make when compliance, noisy neighbors, or per-tenant customization force the issue.
Does HIPAA or SOC 2 require schema-per-tenant or database-per-tenant?
Neither standard mandates physical isolation. Both can be achieved with a pool model and properly enforced row-level security, encryption at rest, and audit logging. That said, when an enterprise customer asks 'is my data in a separate database,' the answer 'yes' (silo) is easier to win deals with than 'no, but we have RLS' (pool). Compliance is sometimes about perception more than the standard's letter.
What is Row Level Security and is it actually safe?
RLS is a Postgres feature that filters rows based on a session variable. You set app.current_tenant at the start of every request, and policies on each table enforce 'WHERE tenant_id = current_setting(app.current_tenant)'. It is genuinely safe when implemented correctly — Supabase, PlanetScale and many YC SaaS rely on it. The risk is forgetting to set the session variable; the mitigation is to enforce it in middleware that runs before any query.
When does pool break down?
Three signals. First, a single noisy tenant degrades performance for everyone (your largest customer's nightly export tanks the database). Second, regulatory or contractual requirements demand physical isolation. Third, per-tenant customization (separate schema versions, custom indexes, different Postgres extensions) becomes necessary. Until you hit one of these, pool is the right call.
Can I migrate from pool to bridge or silo later?
Yes, and most companies that do it migrate selectively — the top 10 enterprise tenants move to silo databases, while the long tail stays in the pool. The migration itself is a sequence of zero-downtime database operations: replicate the tenant's rows to a new database, dual-write briefly, swap reads, drop old rows. Plan for two to four weeks per major tenant migration with careful change management.
Related articles
AI-Native SaaS Architecture in 2026: Patterns That Actually Work
Putting an LLM in the critical path changes everything: cost accounting, deploy gates, retries, caching, observability. Here is the 2026 reference architecture I run with AI-native startups, with real numbers.
Monolith vs Microservices for Early-Stage Startups (2026 Honest Take)
Microservices kill more startups than they save. Ninety-five percent of seed and Series A companies should ship a modular monolith. Here is the honest breakdown of when each architecture wins.
7 Architecture Mistakes That Kill Startups (and How to Avoid Them)
After auditing more than thirty startup codebases, the same seven mistakes show up over and over. Each is fixable cheap on day one and brutal once you have customers.
Want a senior eye on your stack?
If you are scoping an MVP, scaling a SaaS, or staring at an inherited codebase, book a 30-minute call. No pitch deck required.