What is multi-tenant architecture?
Multi-tenant architecture is a software design approach where a single application instance serves multiple customers (tenants), with each tenant's data logically or physically separated from the others. It's the foundation of most SaaS products and determines how you balance cost efficiency, data isolation, and operational complexity.
If you're building a SaaS product, the multi-tenancy question comes up early. It's one of those architecture decisions that's easy to defer and expensive to change later.
Most founders don't think about it until something goes wrong. A customer asks about data isolation. An enterprise prospect requires a dedicated database. A security audit reveals that tenant data is only separated by a WHERE clause that an engineer could accidentally omit.
We've implemented all three major multi-tenant patterns in production systems. Each one has clear tradeoffs. Here's when you'd use each.
Pattern 1: Pooled (shared everything)
All tenants share the same database, the same tables, and the same application instance. Each row in the database has a tenant_id column that determines who it belongs to.
CREATE TABLE invoices (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL REFERENCES tenants(id),
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Every query includes the tenant filter
SELECT * FROM invoices
WHERE tenant_id = 'abc-123'
AND status = 'pending';
When it works: Low-revenue self-serve products. Think $10 to $100 per month per tenant. The economics don't justify separate infrastructure per customer, and the data isolation requirements are minimal.
What's good: Simplest to build and operate. One database to manage, one application to deploy. Query patterns are straightforward. Adding a new tenant means inserting a row, not provisioning infrastructure.
What's risky: A missing WHERE tenant_id = ? clause leaks data across tenants. A single large tenant can impact performance for everyone by running expensive queries or storing disproportionate amounts of data. And if you ever need to provide a customer with a full export or deletion of their data (GDPR right to erasure), you're scanning every table in the database.
How to mitigate the risks: PostgreSQL's Row Level Security (RLS) policies can enforce tenant isolation at the database level, so even if application code forgets the filter, the database won't return rows from other tenants.
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON invoices
USING (tenant_id = current_setting('app.current_tenant')::uuid);
With this in place, you set the tenant context at the beginning of each request (usually in middleware), and every query is automatically scoped. Application code can't accidentally leak data even if a developer forgets the filter.
Pattern 2: Siloed (separate everything)
Each tenant gets their own database instance (and sometimes their own application instance). Complete physical isolation.
When it works: High-revenue enterprise customers, typically paying $5,000 or more per month. Regulated industries where compliance requirements mandate physical data separation. Customers who need to control their own backup schedules, retention policies, or geographic data residency.
What's good: Complete isolation. One tenant's data or performance issues literally cannot affect another's. Compliance is straightforward because there's nothing to separate. Tenant-specific customizations (schema extensions, custom indexes) are possible without affecting anyone else.
What's risky: This is the most expensive pattern to operate. Each tenant is its own infrastructure to monitor, patch, scale, and back up. Database migrations need to run across every tenant instance. A bug fix needs to deploy to every instance. If you have 50 tenants, you have 50 databases.
How to mitigate the risks: Automate everything. Tenant provisioning, schema migrations, deployments, monitoring. If any of this is manual, it doesn't scale past 10 tenants. Infrastructure-as-code, automated migration runners, and centralized monitoring dashboards are mandatory, not optional.
Pattern 3: Bridge (shared application, scoped data)
All tenants share the same application instance, but data is separated at the schema or database level. Each tenant gets their own PostgreSQL schema within a shared database, or their own database on a shared database server.
-- Create a schema per tenant
CREATE SCHEMA tenant_abc123;
CREATE TABLE tenant_abc123.invoices (
id UUID PRIMARY KEY,
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Switch context per request
SET search_path TO tenant_abc123;
When it works: Mid-market products where tenants pay enough to justify some isolation but not enough for dedicated infrastructure. Products where data isolation is important for trust and compliance but physical separation is overkill. Typically $500 to $5,000 per month per tenant.
What's good: Better isolation than pooled without the operational cost of full siloing. Each tenant's data lives in its own schema, so cross-tenant queries are structurally impossible without explicitly switching schemas. Easier to reason about than RLS policies, especially for teams that haven't worked with row-level security before.
What's risky: Schema management gets complex with many tenants. A migration that adds a column needs to run against every tenant schema. Connection pooling can be tricky because you need to set the search path per connection. And there's a practical limit: PostgreSQL handles hundreds of schemas fine, but at thousands of schemas on a single instance, management tooling starts to struggle.
How to decide
The decision usually comes down to three factors.
Revenue per tenant. If you're charging $20 per month, you can't afford dedicated infrastructure per customer. Pooled is the only economically viable option. If you're charging $10,000 per month, the customer can fund their own database instance and they probably expect it.
Compliance requirements. If your customers are in healthcare, finance, or government, they may require physical data separation. Ask early. Finding out after you've built a pooled system that your biggest prospect requires data isolation is expensive.
Team size and operational capacity. Running siloed infrastructure requires automation, monitoring, and incident response at the per-tenant level. If you have two engineers, that operational burden will consume all your capacity. A three-person team should probably start pooled and consider bridge when they grow.
Our recommendation for most startups
Start pooled with Row Level Security enabled from day one.
RLS enforcement gives you the data isolation guarantees of a bridge model without the operational complexity. You can run a single database, a single application instance, and a single deployment pipeline.
If a specific tenant grows large enough to justify dedicated infrastructure, you can extract their data into a separate schema or database at that point. This extraction is well-defined and relatively straightforward if you've kept your queries tenant-scoped from the beginning.
The key is designing the application code as if you might change isolation patterns later, which is the same philosophy we recommend for starting with a monolith. Keep the tenant context clean, enforce boundaries at the data layer, and promote when the economics or compliance requirements justify it.
Don't over-architect for the multi-tenancy model you might need in two years. Build for the model that works now and make sure the boundary is clean enough to change later.
We help SaaS teams make architecture decisions they won't regret. If you're figuring out your multi-tenancy strategy, let's walk through the tradeoffs for your specific product.