Multi-Tenancy Architecture: Building SaaS That Scales Without Chaos
Multi-tenancy is the architectural decision that determines whether your SaaS platform scales smoothly or becomes progressively harder to operate as you add customers. The design you choose in year one tends to persist — migration between tenancy models in production is expensive.
Multi-Tenancy Architecture: Building SaaS That Scales Without Chaos
A SaaS application is a single application serving multiple distinct customers (tenants) from shared infrastructure. The multi-tenancy decision — how to structure the data and isolation layer between tenants — has more long-term operational consequences than almost any other early architecture decision in a SaaS product.
The three main approaches differ significantly in their tenant isolation guarantees, operational complexity, cost at scale, and ease of per-tenant customisation. Understanding the tradeoffs when you have five customers makes the decision reversible. Understanding them after you have five hundred makes it a major migration project.
The Three Tenancy Models
Model 1: Database-per-Tenant
Each tenant has a completely separate database. Tenant A's data exists in db_tenant_a; Tenant B's data in db_tenant_b. The application connects to the relevant tenant's database based on the incoming request (identified by subdomain, API key, or session).
Advantages:
- Complete data isolation — a bug that corrupts one tenant's data cannot affect others
- Simple compliance with data residency or isolation requirements — each tenant's database can be provisioned in any location
- Per-tenant backup, restore, and deletion is straightforward
- Schema customisations per tenant are possible without affecting others
- Performance isolation — a heavy query from Tenant A does not slow Tenant B
Disadvantages:
- Operational complexity grows linearly with tenant count. At 500 tenants, you have 500 databases to manage, monitor, backup, and update when the schema changes
- Schema migrations are exponentially harder — deploying a schema change requires running it against every tenant database
- Resource cost: 500 databases require more infrastructure than a well-optimised shared database
- Cross-tenant analytics (if you want aggregate reports across your customer base) requires federated queries across all tenant databases
Appropriate for: Enterprise SaaS with large tenants that have contractual data isolation requirements, regulatory requirements for data residency, or customisation needs that justify the operational overhead. Typically fewer total tenants (1–50 enterprise clients) where the individual tenant revenue justifies dedicated infrastructure.
Model 2: Shared Database, Separate Schemas
A single database serves all tenants, but each tenant has their own schema within it. In PostgreSQL: tenant_abc.orders, tenant_abc.customers, tenant_xyz.orders, tenant_xyz.customers. Tables are isolated by schema ownership.
Advantages:
- Simpler than database-per-tenant while maintaining stronger isolation than row-level segmentation
- Schema migrations are easier — run against a single database, though still need to apply to each tenant's schema
- Some operational leverage compared to separate databases
Disadvantages:
- Managing schema migration across hundreds of schemas is still complex
- PostgreSQL has practical limits on the number of schemas in a database
- Cross-tenant analytics more straightforward than database-per-tenant but still requires schema-qualified queries
- Less common in modern tooling — ORM support for schema switching adds complexity
Appropriate for: Mid-market SaaS with dozens of tenants where schema isolation is important but full database-per-tenant is operationally excessive.
Model 3: Shared Database, Shared Schema with Row-Level Security
A single database, a single schema, all tenants' data in the same tables. Every table has a tenant_id column. Row-Level Security (RLS) in PostgreSQL enforces that each database connection can only access rows belonging to its tenant.
Advantages:
- Lowest operational complexity — one database, one schema, one migration to run
- Best operational leverage — aggregate analytics, cross-tenant reporting, and operational queries work naturally
- Most resource-efficient — shared infrastructure with no per-tenant overhead
- Simplest schema migration process
Disadvantages:
- Shared resources — a very expensive query from one tenant can affect system performance for others
- Security through software (RLS policies) rather than infrastructure isolation — a policy bug theoretically exposes cross-tenant data
- Data residency requirements are harder to meet
- Per-tenant customisation of data structures is more limited
Appropriate for: Most SaaS products serving mid-market or SMB customers where the tenant count is large, individual tenants are not enterprise-scale, and operational simplicity is a priority.
PostgreSQL Row-Level Security Implementation
For the shared schema model, PostgreSQL's native RLS provides a robust isolation layer at the database level:
-- Enable RLS on the orders table
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- Create a policy that restricts access to the current tenant
CREATE POLICY tenant_isolation ON orders
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
The application sets the tenant context at the start of each request and all queries against that connection automatically filter to that tenant's rows:
// Set tenant context when establishing or borrowing a connection
await pool.query(`SET app.current_tenant_id = '${tenantId}'`);
// All subsequent queries on this connection are automatically filtered
const orders = await pool.query('SELECT * FROM orders'); // Only this tenant's orders
Security considerations: The application must set app.current_tenant_id before every query that should be tenant-isolated. A programming error that skips this setting could result in cross-tenant data access. Testing for this specific failure mode — queries without tenant context returning unexpected rows — should be in the test suite.
Performance: Adding tenant_id to every query's WHERE clause might seem to invalidate indexes. In practice, including tenant_id in the leading column of composite indexes on frequently queried tables maintains query performance:
CREATE INDEX idx_orders_tenant_date ON orders (tenant_id, created_at DESC);
Tenant Provisioning and Onboarding
Regardless of model, tenant provisioning should be automated:
Shared schema: When a new tenant signs up, create a record in the tenants table; generate a tenant UUID; configure the tenant's settings; optionally seed any default data for the tenant. No infrastructure changes required.
Database-per-tenant: Provisioning involves creating a new database, running the full migration set, creating the application user credentials, storing the connection details securely, updating the tenant routing configuration. This is a multi-step process that takes seconds but requires infrastructure automation (Terraform, a provisioning script with proper secret management).
Tenant offboarding: Equally important. When a tenant churns or their contract ends, the data deletion or retention process should be defined and automated. Under NDPR and GDPR, data must be deleted or returned on contract termination per the data processing agreement. For shared schema tenants, this is a targeted delete of all rows with that tenant_id. For separate databases, it is dropping the database after export.
The Operational Scaling Threshold
The practical observation from operating SaaS at scale: shared schema with RLS works well up to hundreds or thousands of tenants. The point at which it becomes constraining is when individual tenants generate such volume that shared table performance becomes a concern — typically when a single tenant's data represents >10–15% of total table volume.
The hybrid approach used by many mid-scale SaaS products: shared schema for standard tenants, database-per-tenant for enterprise clients with contractual isolation requirements. The enterprise tier pricing justifies the operational overhead; the SMB tier benefits from shared infrastructure efficiency.
When we built multi-tenant systems for Nigerian SaaS companies, the single biggest architectural decision was data isolation. Nigerian enterprise clients — particularly in financial services and healthcare — consistently require proof that their data is logically or physically separated from other tenants. The schema-per-tenant model costs more to operate but closes enterprise deals that shared-schema cannot. We have seen RFPs from Nigerian banks and insurance companies where tenant isolation was not a scored criterion — it was a pass/fail gate.
This hybrid requires the application to handle two connection models — a routing layer that determines whether a request should use the shared database (most tenants) or a dedicated database (enterprise tenants). Not trivially complex, but manageable with a well-designed connection factory pattern.
What Changes Most as You Scale
The decision that becomes most painful at 500+ tenants is not tenancy model — it is the schema migration process. Any change to the database schema needs to deploy to all tenants without breaking running traffic. This requires:
- Blue/green or rolling schema migrations (backward-compatible changes that can run before the code change)
- Migration orchestration that can run across all tenant databases in parallel without downtime
- Versioned migrations with the ability to roll back cleanly
For shared schema, this is a single migration run. For separate databases, this is a migration runner that parallelises across all tenant databases and handles failures in individual databases gracefully. Tools: Flyway and Liquibase provide multi-database migration support; custom scripts using database connection pooling can parallelise migrations across hundreds of databases.
The architecture decision you make for five customers will still be your architecture when you have five hundred. Choose with the operational complexity at scale in view, not just the simplicity of initial implementation.
Related Articles
- Microservices vs Monolith for Nigerian Businesses — Choosing the right architecture pattern
- Database Choices for Nigerian Business Applications — How database choices affect multi-tenant design
- Building for SOC 2 from Day One — Security architecture for SaaS products