By Swetha Golla · ~6 min read · Senior Application Architect
🔗 This post has a live interactive version with a clickable Silo / Pool / Bridge comparison toggle (isolation strength, cost at ~500 tenants, and blast radius per model) plus a diagram of all three isolation models: read it here
TL;DR
- AWS's SaaS Factory program gave the industry its working vocabulary for this decision: silo (dedicated infra/DB per tenant), pool (shared schema, rows tagged by
tenant_id), and bridge (a deliberate mix). - Pool is the cheapest model to run at hundreds of tenants, but its isolation is only as strong as your query discipline — a missing
WHERE tenant_id = ?is a real, recurring data-leak bug class, not a hypothetical. - Silo gives you the cleanest blast-radius and compliance story, but N tenants means N databases to patch, back up, and capacity-plan, and cross-tenant analytics means fanning out across all of them.
- Most production systems, and nearly every regulated one, end up bridge — and the split is usually decided per data type (cardholder data vs. everything else), not once for the whole platform.
The Setup
You're the architect on "PayLoop," a fictional B2B payments platform at 40 tenants and climbing. Onboarding tenant #41 — a mid-size retailer — triggers their security questionnaire, and question 12 is the one that always lands on your desk: "Describe how customer data is logically or physically separated between tenants." Your answer today is "a tenant_id column and an ORM scope." That answer is true, it's normal, and it is about to get harder to defend as this platform starts touching settlement data and stored card references for tenants who each expect their compliance boundary to be someone else's problem, never theirs.
The Vocabulary the Industry Actually Uses
This isn't a new problem, and you don't have to invent the framing from scratch. AWS's SaaS Factory program formalized it in the SaaS Tenant Isolation Strategies whitepaper: tenants can be deployed in a fully silo model with dedicated resources, a pool model sharing infrastructure for efficiency and scale, or a bridge model that mixes the two — pooling some parts of the system while siloing others. The AWS Well-Architected SaaS Lens adds the detail that matters most in practice: in a system decomposed into services, isolation is decided per service, sometimes per data store, not once for the whole platform. That's the mental model worth stealing, independent of whether you run on AWS.
One caveat worth flagging directly: AWS isn't fully consistent with itself here — an older AWS storage whitepaper and the 2020 AWS Database Blog post (both linked below) use "bridge" more narrowly, to mean one shared database with a separate schema per tenant, not the broader "mix of silo and pool" sense used throughout this piece. Don't assume everyone in a room means the same thing by the word.
(The live version has a diagram of all three models — silo's separate databases, pool's single tagged table, and bridge routing by tenant tier — worth a look if you want the visual before the prose.)
What Actually Changes Between the Three Models
Isolation strength and operating cost move in opposite directions as you go from silo to pool, and the honest way to reason about it is blast radius, not vibes. In a silo model, a bug in tenant A's query path can only ever touch tenant A's database — there is no shared table for it to reach across. That's a real property, not marketing: it's structural, not policy-enforced. The price is operational: 500 tenants in a silo model is 500 databases to patch, monitor, back up, and capacity-plan, and a report spanning tenants means fanning a query out across all of them and merging results in application code.
Pool inverts both sides of that trade: one schema, one set of migrations, one connection pool to tune, and it scales to thousands of tenants without your ops headcount scaling with it. But every tenant's rows sit in the same table, so isolation lives entirely in application code (or a database-enforced layer) remembering to filter — and shared compute means one tenant's traffic spike degrades everyone else's latency, the classic noisy-neighbor problem AWS calls out explicitly in the same whitepaper.
The Bug Class That Makes Pool a Real Risk, Not a Hypothetical
"We'll just always filter by tenant_id" sounds like a process fix until you look at where these bugs actually live: not in the well-tested primary API path, but in the report someone wrote in an afternoon, the admin tool, or the background job that queries across a join and forgets the filter three tables deep. That shape of bug is functionally an IDOR (insecure direct object reference) — it compiles, it runs, it returns rows, and nothing about the code looks broken until you check whose data came back. It's exactly why AWS's own Database Blog recommends PostgreSQL Row-Level Security as a defense-in-depth backstop for pooled multi-tenant systems: RLS makes the database itself enforce the tenant filter on every query, so a forgotten WHERE clause fails closed instead of leaking silently.
The Trade-Offs, Honestly
| Isolation | Ops cost at scale | Cross-tenant analytics | |
|---|---|---|---|
| Silo | Structural — a bug can't reach another tenant's data. | Linear in tenant count — N databases to patch/back up/monitor. | Fan-out query across every tenant DB, merge in app code. |
| Pool | Policy-enforced — as strong as your query discipline (or RLS). | Roughly flat — one schema scales to thousands of tenants. | A single GROUP BY tenant_id away. |
| Bridge | Tunable — strong where you need it, cheap where you don't. | Two operating models to run instead of one — real, but bounded. | Depends which side each tenant landed on. |
Where Compliance Actually Forces the Decision
In payments, this stops being an architecture-review debate and starts being a compliance requirement. The PCI Security Standards Council's own scoping and segmentation guidance doesn't mandate a specific architecture, but it's explicit that segmentation is the accepted way to shrink your cardholder data environment (CDE) — and that any connection across that boundary needs to be justified, restricted, and periodically re-validated with penetration testing. That pushes cardholder data toward stronger isolation almost by default, while giving you no such pressure on, say, tenant UI preferences or notification settings.
- This is exactly the bridge pattern AWS describes in its own whitepaper: the web/application tier pooled and shared across all tenants, while the tier holding the sensitive data is siloed or partitioned — decided per layer, not once for the whole stack.
- The decision is per data type, not per system. A payments platform can legitimately pool tenant metadata, configuration, and analytics while siloing (or applying stricter partitioning to) anything that touches cardholder data or settlement records.
The mistake I see most often isn't picking the wrong model — it's picking one model and applying it uniformly, so either the compliance-sensitive data is under-isolated, or the low-risk data is paying full silo tax for no reason.
My Take
Pure silo and pure pool are both easy to explain in a design review and both wrong for a platform that lives past its first dozen tenants. Pure silo turns your platform team into database administrators at scale — you will be patching CVEs across hundreds of instances long before you're building product. Pure pool turns every engineer who touches a query into your isolation boundary, forever, and that's a bet I've watched lose in production: the leak is never in the code someone reviewed carefully, it's in the report nobody thought was sensitive. Bridge isn't a compromise you settle for — it's the only model that lets you spend your isolation budget where a leak is actually expensive and skip it where it isn't. Decide per data type, put a database-enforced backstop (RLS or equivalent) under whatever you pool, and revisit the split when a tenant's size or a regulator's requirement changes — because it will.
Rule of thumb: Pool by default for cost and velocity, but silo or add a database-enforced backstop for any data type where a leak would be a regulatory incident rather than an embarrassment — and make that call per data type, not once for the whole platform.
See the working example → A small, stdlib-only demo showing a pool-model query leak side-by-side with the same bug in a silo model, where it structurally can't leak. Disagree with where the line should sit? Tell me which data type you'd have split differently — I'd genuinely like to hear it.
Sources & Further Reading
- AWS Whitepaper — "SaaS Tenant Isolation Strategies: Isolating Resources in a Multi-Tenant Environment"
- AWS Well-Architected Framework — "Silo, Pool, and Bridge Models — SaaS Lens"
- AWS Whitepaper — "SaaS Storage Strategies: Partitioning Models"
- AWS Database Blog — "Multi-tenant data isolation with PostgreSQL Row Level Security"
- PCI Security Standards Council — "Guidance for PCI DSS Scoping and Network Segmentation"
Top comments (0)