Multi-Tenant SaaS Architecture Explained: Silo, Pool, Bridge, Cells (2026)

Oct 20, 2026 · 12 min · Ajay Kumar

Every SaaS product I have worked on hit the same argument somewhere between the tenth and the hundredth customer: should the next big customer get their own database, or go in with everyone else? The answer usually got decided by whoever was on call that week, which is how you end up with three tenancy models in one codebase and nobody able to say which tenants live where. This post explains multi-tenant SaaS architecture properly: the silo, pool and bridge models from the AWS SaaS Lens, what database-per-tenant versus shared schema really costs you, why noisy neighbours are a governance problem rather than a capacity one, where cell-based architecture fits, and how to get a cost-per-tenant number that is good enough to price on.

What is multi-tenant SaaS architecture?

A tenant is the unit you isolate and bill. In B2B it is usually a customer organisation with many users; in B2C it might be a person, or a family, or a club. Microsoft's tenancy models guide makes the point that defining the tenant is the first design decision, because one customer can need several tenants (separate regions, or separate dev and prod) and that changes everything downstream.

Multi-tenancy means tenants share some part of the running system. The same guide is blunt about the alternative: with fully dedicated deployments, "100 tenants probably require 100 times that cost", plus the operational drag of patching 100 copies. With fully shared deployments you get the economics, and in exchange you own data separation, per-tenant performance, and the fact that one bad deploy hits every customer at once.

The important framing is that isolation is a spectrum you choose per component, not one switch for the whole product. Your web tier can be shared while your database is dedicated. That is where the AWS vocabulary helps.

Silo, pool and bridge: the AWS SaaS Lens models

The AWS Well-Architected SaaS Lens defines three patterns:

  • Silo: tenants get dedicated resources, anything from a separate database to a fully independent stack. The Lens adds a qualifier that matters: a silo still relies on shared identity, onboarding and operations. Without that, you are running a managed service, not SaaS.
  • Pool: tenants share resources (compute, storage, messaging) to get economies of scale. This is the classic picture of multi-tenancy.
  • Bridge: a mix, where some services are siloed and others pooled. The Lens says a service's regulatory profile and noisy neighbour attributes might push it to silo, while agility and cost push another service to pool.

AWS's Guidance for Multi-Tenant Architectures maps the three onto databases: silo as a database instance per tenant, bridge as a schema per tenant, pool as shared tables protected by the engine's row-level security. Microsoft uses different words for the same shapes: automated single-tenant deployments, fully multitenant, and vertically or horizontally partitioned deployments.

Model What is shared Isolation Cost per tenant Main risk Source
Silo Identity, onboarding, ops tooling Strongest Highest, scales linearly Fleet sprawl, slow upgrades AWS SaaS Lens, Azure
Bridge Some tiers (often web and app) Per service Medium Two code paths to test AWS SaaS Lens
Pool Everything, tenant_id on every row Logical only Lowest Data leak bugs, noisy neighbours, global blast radius AWS guidance
Cells A copy of the whole stack per group of tenants Fault isolation between cells Close to pool Router and control plane complexity AWS cell-based whitepaper

The SaaS Lens also describes targeted isolation: silo only the service that needs it, for only the tenants that need it. In practice this is where most mature products land. The free and mid tiers live in the pool, the enterprise tier pays for a dedicated database, and the order-processing service that a bank cares about is siloed while the notification service is not.

Three tenancy models, from most isolated to most shared Silo app A app B app C db A db B db C Isolation: strongest Cost: highest, linear in tenants Noisy neighbours: none Upgrades: one rollout per tenant DB shape: instance per tenant Bridge shared web and app tier schema A schema B schema C Isolation: at the data tier Cost: medium Noisy neighbours: app tier only Upgrades: one deploy, N migrations DB shape: schema or db per tenant Pool shared app tier shared tables, tenant_id + RLS Isolation: logical only Cost: lowest Noisy neighbours: need quotas Upgrades: one deploy, all tenants DB shape: shared schema Cells wrap any model: a full copy of the stack per group of tenants, behind a thin router. AWS example: 10 cells serving 100 requests, one cell fails, about 90% of requests are unaffected.
Silo, bridge and pool as defined in the AWS SaaS Lens, with database shapes from AWS's multi-tenant guidance. The cell figure is the worked example from AWS's cell-based architecture whitepaper.

Database per tenant vs shared schema: which should you pick?

The database is where the tenancy decision actually bites, because it holds the data that leaks and the queries that hog. There are three shapes.

Database per tenant. Isolation is physical and easy to explain to an auditor. Per-tenant backup, restore and deletion are trivial, and a tenant who wants to leave can be handed a dump. The costs arrive later: every migration runs N times and some fail halfway, connection counts multiply, and cross-tenant analytics needs a separate pipeline. Azure's guide notes you must plan how to "query and manipulate data across multiple deployments" from the start.

Schema per tenant. One database, one schema per tenant, with search_path or explicit qualification picking the right one. It feels like a cheap silo. In Postgres the catch is catalog growth: thousands of schemas times dozens of tables is a lot of relations for the planner, autovacuum and pg_dump to walk, and migrations still loop over every schema. It works well for tens or low hundreds of tenants and gets uncomfortable beyond that.

Shared schema with a tenant_id column. Every tenant-owned table carries tenant_id, usually as the leading column of the primary key and of every index. This is the cheapest to run and the easiest to migrate, and it is the shape every large pooled SaaS converges on. Notion's 2021 write-up on sharding Postgres is a good example of where it leads: they partitioned by workspace ID into 480 logical shards spread across 32 physical databases, which only works because the tenant key was already on every row.

The danger of the shared schema is that isolation depends on every query remembering WHERE tenant_id = ?. AWS's prescriptive guidance says row-level security is "required to maintain tenant data isolation in a pooled model with PostgreSQL" and prefers a runtime variable set by the application over a database role per tenant. I agree, with caveats that matter in production: table owners and BYPASSRLS roles skip policies unless you FORCE ROW LEVEL SECURITY, connection poolers in transaction mode will leak a session-level SET to the next client, and Postgres has shipped RLS bugs (CVE-2024-10976, fixed in 17.1 and siblings; CVE-2025-8713, fixed in 17.6 and siblings). I wrote the step-by-step implementation, including those pitfalls with real psql output, in the companion guide: how to implement multi-tenancy with Postgres row-level security.

What is the noisy neighbour problem, and how do you contain it?

Microsoft's noisy neighbour antipattern page defines it as one tenant's performance degrading "because of the activities of another tenant". Two variants matter. The obvious one is a single heavy tenant: the customer who runs a 40-minute export at 9am. The subtle one is many small tenants with correlated peaks, none of them individually large, that together exceed capacity at the top of the hour.

The useful insight from that page is to treat noisy neighbours as a resource governance problem, not a capacity problem. You cannot buy your way out of it, because the next big tenant will find the new ceiling. The controls that work, roughly in the order I would add them:

  1. Per-tenant telemetry. Put tenant_id on every log line, span and metric label you can afford (and on high-cardinality ones, in exemplars rather than labels). You cannot govern what you cannot attribute.
  2. Rate limits and quotas per tenant and per tier, enforced at the edge and at expensive internal operations, with clear 429s.
  3. Query guardrails: statement_timeout per role, result-size caps, and pushing heavy exports onto queues with per-tenant concurrency limits.
  4. Move the outlier. This is the bridge model in action: promote the heavy tenant to a dedicated database or a dedicated worker pool, and charge for it.

Compute isolation is part of the same story. If tenants run their own code (plugins, notebooks, agents), a shared process is not a tenancy boundary at all. That is the problem I work on with PandaStack, an open-source Firecracker microVM cloud I build and run, and I have written separately about choosing between containers, microVMs and gVisor for exactly this.

What is cell-based architecture, and do you need it?

Pooling makes every deploy and every poison-pill request a global event. Cells are the answer to that, and they are orthogonal to silo and pool. AWS's cell-based architecture whitepaper describes a cell as a complete, independent copy of the workload that shares no state with other cells, with a "thinnest possible" router that sends each request to a cell by a partition key such as customer ID, and a control plane that provisions cells and migrates customers between them. Its example: with 10 cells serving 100 requests, a failure in one cell leaves 90% of requests unaffected. It also notes that cells do not have to mean more hardware; the same 30 hosts can be regrouped behind a router.

Microsoft calls the same idea deployment stamps. Slack's version is instructive because it was driven by a real incident: after a June 2021 network fault in one availability zone caused user-visible errors, they spent about 1.5 years making each AZ a cell that can be drained, with a goal of removing traffic from an AZ within 5 minutes and the ability to shift weights in 1% steps.

Do you need cells? Not at 50 customers. You need the precondition for cells, which is a tenant-to-deployment mapping that every request consults. If that mapping exists from day one, adding a second cell is an operational project. If tenant placement is implicit, it is a rewrite. Here is the minimal tenant catalog I would create on day one, on PostgreSQL 18:

-- PostgreSQL 18. Tenant catalog: the control plane's source of truth.
-- Every request resolves tenant -> tier -> cell before touching data.
CREATE TABLE control.tenants (
  tenant_id   uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  slug        text NOT NULL UNIQUE,              -- used in hostnames
  tier        text NOT NULL CHECK (tier IN ('free', 'pro', 'enterprise')),
  isolation   text NOT NULL DEFAULT 'pool'
              CHECK (isolation IN ('pool', 'silo')),
  cell        text NOT NULL DEFAULT 'cell-01',   -- which stack serves it
  db_dsn_ref  text,                              -- secret name, only for silo
  status      text NOT NULL DEFAULT 'active'
              CHECK (status IN ('active', 'suspended', 'migrating')),
  created_at  timestamptz NOT NULL DEFAULT now()
);

-- The router's only query. Cache it at the edge with a short TTL.
SELECT cell, isolation, db_dsn_ref, status
FROM control.tenants
WHERE slug = $1;

The migrating status is the part people skip. Moving a tenant between cells, or from pool to silo, is a write freeze, a copy, a flip of cell, and a cache purge. If the router does not understand that state, migrations become outages.

How do you calculate cost per tenant?

Pricing, tiering and the decision to silo someone all depend on knowing what a tenant costs, and in a pool nothing on the cloud bill says so. Tod Golding's AWS post on calculating tenant costs is from 2017 and still the clearest method: siloed resources are attributed directly through accounts or tags; pooled resources are apportioned by measured activity (requests per tenant for compute, storage footprint and IOPS for data); then the shares are multiplied against the bill. His line that the goal is data "good enough" is the right bar. The SaaS Lens expenditure awareness section makes the same two-step split: meter consumption, then correlate with cost.

Two refinements from experience. First, apportion each resource with its own driver; blending database and egress into one "activity" score hides the tenant who is cheap on CPU and ruinous on storage. Second, LLM tokens are now often the largest per-tenant variable cost in a SaaS product, and they are trivially attributable if you tag calls with tenant_id at the gateway. I covered that side in FinOps for AI token costs, and it connects directly to the pricing shift in seats dying in the 2026 SaaS repricing: you cannot price on outcomes if you cannot cost per tenant.

FAQ

What is the difference between single-tenant and multi-tenant SaaS?

Single-tenant gives each customer a dedicated deployment; multi-tenant shares some or all of the stack. In AWS terms single-tenant is a full silo, while multi-tenant covers pool and bridge. Most products are a mix, with dedicated deployments sold as an enterprise tier.

Is row-level security enough for tenant isolation?

It is the right enforcement layer for a pooled Postgres database, but only if the application role cannot bypass it, the tenant context is set per transaction, and you test cross-tenant access in CI. It does nothing for noisy neighbours or for shared caches and object storage, which need their own tenant scoping.

When should a tenant move from pool to silo?

When a contract requires it (data residency, dedicated encryption keys, audit scope), when the tenant's load measurably degrades others despite quotas, or when they will pay for it. Make the move a routine control-plane operation rather than a bespoke project.

Is cell-based architecture the same as sharding?

Related but not identical. Sharding splits data across databases; a cell is a complete copy of the whole workload, compute included, that shares no state with other cells. A sharded database behind one shared app tier still has a global blast radius for bad deploys.

This quarter, if I were running a pooled SaaS product, I would do three things: add a tenant catalog with tier, cell and status columns even if there is only one cell; turn on FORCE ROW LEVEL SECURITY with a non-owner application role and a CI test that tries to read another tenant's rows; and get a rough cost-per-tenant report out of the gateway and request logs. None of that is new technology. It is the same platform discipline as ever, and it is what lets you say yes to the next enterprise customer without a migration.

Related: Seats Are Dying. What the 2026 SaaS Repricing Looks Like From the Compute Layer, Container vs microVM vs gVisor: Choosing Agent Isolation and FinOps for AI in 2026.

I'm Ajay Kumar — I build and operate PandaStack, an open-source Firecracker microVM cloud for AI agents, and write about the infrastructure and tooling around it.

Need this kind of infrastructure work? See what I do or email hello@ajayk.sh.


Related