
TLDR
- There are three main multi-tenancy patterns: shared database with tenant_id columns, schema-per-tenant, and database-per-tenant. Each has different cost, isolation, and complexity trade-offs.
- Shared database is the right starting point for almost every SaaS. It is cheap, easy to operate, and scales further than people think.
- Move to schema-per-tenant or database-per-tenant only when you have specific isolation, compliance, or performance reasons. Migrating later is doable but not trivial.
- Whichever pattern you pick, enforce tenant isolation at the database layer (row-level security in PostgreSQL is excellent for this), not just in application code.
- The real architecture mistake is not the pattern. It is missing tenant context in queries, causing data to leak between tenants.
Multi-tenant architecture is the part of SaaS engineering that founders often hand to developers without a clear answer to "how do we keep tenants separated?" The default answer of "we will figure it out as we go" leads to architectural decisions that are extremely expensive to change later.
Here are the three main patterns and how to pick.
Pattern 1: Shared Database, Shared Schema
All tenants live in the same database, in the same tables. Every table has a tenant_id (or organization_id) column. Queries always filter by tenant_id. Application code is responsible for never accidentally querying without that filter.
Pros:
- Cheapest to operate. One database, one set of indexes, one connection pool.
- Easiest to deploy schema changes. One migration runs once.
- Supports the most tenants. A well-tuned PostgreSQL can handle tens of thousands of tenants this way.
- Aggregate analytics across all tenants is trivial.
Cons:
- Tenant isolation is enforced by application code. A missing WHERE clause in a query is a data leak.
- Noisy neighbors can affect performance for everyone.
- Hard to give a single tenant a custom database tweak (different region, different backup schedule, etc.).
- Compliance audits will ask hard questions.
When to use: Almost always, especially for the first 1000 tenants. Use PostgreSQL row-level security policies to enforce isolation at the database layer, not just in code.
Pattern 2: Shared Database, Schema Per Tenant
One database, but each tenant gets their own schema (in PostgreSQL terms). All tables exist in each schema. The application sets the search_path to the right schema based on the current tenant.
Pros:
- Stronger isolation. Tables for tenant A are physically separate from tables for tenant B.
- Easier to back up or restore a single tenant.
- Easier compliance story. You can show a clear logical separation.
- Still relatively easy to operate (one database).
Cons:
- Schema migrations get harder. Adding a column means running a migration in every schema.
- Cross-tenant queries (for analytics, admin tools) get awkward.
- Connection pooling becomes more complex.
- Number of schemas has practical limits (PostgreSQL handles thousands fine, but tens of thousands gets weird).
When to use: Mid-stage SaaS with 100-5000 tenants where compliance or isolation requirements are real. Often the right migration target from shared schema once it gets uncomfortable.
Pattern 3: Database Per Tenant
Each tenant gets their own dedicated database. Often used for enterprise customers who require complete data isolation.
Pros:
- Complete isolation. Tenant A's database failure has no impact on tenant B.
- Per-tenant customization is easy (different region, different version, different backup).
- Easiest compliance story. Customers can audit their own database.
- Very simple per-tenant operations.
Cons:
- Most expensive to operate. Hundreds of tenants means hundreds of databases.
- Cross-tenant analytics requires aggregating across databases. Hard.
- Schema migrations require running across every tenant database. Slow.
- Connection pooling and operational overhead is significant.
When to use: Enterprise SaaS with high-value customers who pay enough to justify dedicated infrastructure. Often combined with one of the other patterns for smaller customers.
The Hybrid Approach
Many mature SaaS platforms run a hybrid: shared database with row-level security for small customers, dedicated database for enterprise customers who require it. The application abstracts the difference so customers do not know which model they are on.
This is the right model for any SaaS that serves both SMB and enterprise. It is also the model that requires the most careful architectural planning, because the abstraction layer that hides the difference between models has to be solid.
Enforce Isolation at the Database Layer
Whatever pattern you pick, do not rely on application code to enforce tenant isolation. The pattern that prevents disaster is using PostgreSQL's row-level security to make the database itself enforce that no query can return rows from another tenant.
The setup looks like this:
- Add a tenant_id column to every table
- Create a policy on each table that says "rows are visible only when tenant_id = current_tenant"
- At the start of each request, set the database session's current_tenant variable to the authenticated user's tenant
- Now every query the application runs is automatically scoped to the right tenant, no matter what the application code looks like
This single pattern has prevented more SaaS data breaches than any other technical decision we know of.
The Migration Path
You can migrate between patterns later. It is not free, but it is not catastrophic either. Common migration paths:
- Shared database to schema-per-tenant: extract each tenant's data into its own schema, update the application to set search_path on connection
- Schema-per-tenant to database-per-tenant: migrate each schema to a new database, update connection routing
- Shared anything to dedicated database for one tenant: extract that tenant's data and stand up dedicated infrastructure
The migration is much easier if your application code was written assuming you might migrate. Use a tenant context object that gets set per request, and route database connections through it. Then the migration is mostly an infrastructure change, not a code change.
What to Pick for V1
For your first version, pick shared database with row-level security. It is the cheapest, the easiest to operate, the easiest to develop against, and it scales further than you think. You can graduate to other patterns later if and when you have a specific reason to.
The wrong move is to over-architect for a future scale you may never reach. We have seen pre-revenue SaaS startups try to ship database-per-tenant architectures because "enterprise will demand it eventually." They run out of runway before any enterprise shows up.
At Stunzer Digital, multi-tenant SaaS architecture is one of our core specialties. If you are building a SaaS and want to get the foundation right the first time, we can help scope the right pattern for where you are now and where you are going.
Tags
Related service
Want this built? See how we work on Web Development.


