Multi-Tenancy Design: [SaaS product or platform name]
Design document for the multi-tenant architecture of a SaaS product:
tenancy model (silo, pool, bridge) and trade-offs, tenant identification
and context propagation, data isolation, noisy neighbour controls,
tenant onboarding and offboarding, per-tenant configuration and feature
flags, security and compliance including data residency, per-tenant
observability, cost allocation, scaling and migration between tiers.
Based on common SaaS architecture practice. Replace every [bracketed]
value and every row marked Example; the examples follow a B2B project
management product with about 400 business tenants.
Document control
| Field | Value |
|---|
| Document ID | [MTD-001] |
| Product | [Name] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Superseded] |
| Author(s) | [@architect] |
| Reviewers | [@security-architect], [@data-protection-officer], [@sre-lead] |
| Last updated | YYYY-MM-DD |
| Classification | [Internal / Confidential] |
| Related | [System design], [Security architecture], [ADR on tenancy model], [Cost model] |
Approvals
| Role | Name | Decision | Date |
|---|
| Chief architect | [@name] | [Approved / Changes requested] | YYYY-MM-DD |
| Security | [@name] | [Decision] | YYYY-MM-DD |
| Data protection | [@name] | [Decision] | YYYY-MM-DD |
| SRE / operations | [@name] | [Decision] | YYYY-MM-DD |
Revision history
| Version | Date | Author | Change summary |
|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft |
| 0.2 | YYYY-MM-DD | [@name] | Example: Added bridge model for Enterprise tier after security review |
| 1.0 | YYYY-MM-DD | [@name] | Example: Approved |
Contents
1. Introduction
1.1 Purpose
This document defines how [Product] serves many customer organisations
(tenants) from shared infrastructure while keeping their data, performance
and configuration isolated. It is the reference for engineering, security
and operations decisions that depend on tenancy.
1.2 Definitions
| Term | Meaning |
|---|
| Tenant | A customer organisation with its own users, data and configuration |
| Tier | A commercial plan that determines isolation level, quotas and features |
| Tenant context | The verified tenant identity attached to a request or message |
| Silo | Dedicated resources for one tenant |
| Pool | Resources shared by many tenants |
| Bridge | A mix: some layers pooled, others siloed |
1.3 Tenant profile
| Tier | Tenants (now / in 2 years) | Users per tenant | Data per tenant | Price per month | Isolation expectation |
|---|
| Starter | Example: 300 / 2,000 | 5-50 | < 5 GB | USD 300 | Logical isolation |
| Business | Example: 90 / 400 | 50-1,000 | 5-200 GB | USD 4,500 | Logical isolation, higher quotas |
| Enterprise | Example: 10 / 40 | 1,000-20,000 | 200 GB-5 TB | Contract | Dedicated data store, region choice |
2. Tenancy requirements
| ID | Requirement | Priority | Verification |
|---|
| MT-01 | Example: No request can read or modify another tenant's data, even with an application bug in a query | Must | RLS tests; penetration test with cross-tenant attempts |
| MT-02 | Example: A single tenant cannot raise p95 latency for others by more than 20% | Must | Load test with one tenant at 10x normal traffic |
| MT-03 | Example: New Starter or Business tenant is usable within 2 minutes of sign-up | Must | Onboarding pipeline timing |
| MT-04 | Example: Enterprise tenants choose EU or US data residency | Must | Deployment configuration audit |
| MT-05 | Example: Tenant data fully deleted within 30 days of contract end | Must | Offboarding job evidence |
| MT-06 | Example: Cost per tenant reported monthly | Should | Finance report |
| MT-07 | Example: Move a tenant between tiers with < 15 min write downtime | Should | Migration rehearsal |
| [MT-nn] | [Requirement] | [Must / Should / Could] | [Method] |
3. Tenancy model
3.1 Options
| Dimension | Silo (dedicated per tenant) | Pool (shared by all) | Bridge (mixed per layer) |
|---|
| Isolation strength | Strongest; separate resources | Logical only; depends on code and database controls | Strong where siloed, logical elsewhere |
| Cost per tenant | Highest; idle capacity per tenant | Lowest; high utilisation | Medium |
| Noisy neighbour exposure | None between tenants | Highest; needs quotas and limits | Limited to pooled layers |
| Operations at scale | Many deployments to upgrade and monitor | One deployment | Several, grouped by tier |
| Onboarding speed | Minutes to hours (provisioning) | Seconds (a row in a table) | Depends on tier |
| Per-tenant customisation | Easy | Hard | Medium |
| Compliance and residency | Simple to prove | Needs per-tenant controls and evidence | Siloed layer covers strict tenants |
3.2 Decision
| Layer | Starter | Business | Enterprise |
|---|
| Edge and API gateway | Pool | Pool | Pool (per-region) |
| Application services | Pool | Pool | Pool (per-region) |
| Primary database | Example: Pool: shared cluster, row-level security | Pool: shared cluster, row-level security | Silo: dedicated database per tenant |
| Search index | Pool, tenant-filtered | Pool, tenant-filtered | Silo index |
| Object storage | Pool, tenant prefix | Pool, tenant prefix | Silo bucket with tenant-managed key |
| Background workers | Pool, fair queues | Pool, fair queues | Pool with reserved concurrency |
Rationale: [summary and ADR link]. Example: pooled compute keeps
Starter unit cost under USD 20 per month; siloed data for Enterprise
meets contractual isolation and residency requirements without running
a separate application stack per customer (ADR-031).
4. Tenant identification and context propagation
4.1 Identification
| Channel | How the tenant is identified | Verification |
|---|
| Web app | Example: Subdomain acme.example.app resolves to tenant | Must match tenant_id claim in the session token |
| Public API | Example: API key or OAuth client bound to one tenant | Key lookup in tenant registry |
| Webhooks inbound | Example: Per-tenant signing secret | Signature check |
| Background jobs | Tenant ID in job envelope | Set by producer from verified context only |
4.2 Context propagation
Tenant context is resolved once at the edge and carried in signed tokens
and message headers. The sequence shows a request through to the
database.
Rules: [list]. Example: services reject requests without tenant
context; the context library sets the database session variable on every
connection checkout and clears it on return; events without a
tenant_id header are dead-lettered; logs and traces include
tenant_id automatically.
5. Data isolation
| Pattern | Description | Pros | Cons | Used for |
|---|
| Database per tenant | Separate database or cluster | Strong isolation, easy restore per tenant | Cost, many schemas to migrate | Example: Enterprise |
| Schema per tenant | One database, one schema per tenant | Moderate isolation | Catalogue bloat beyond a few thousand schemas | Not used |
| Shared schema with tenant column | All rows carry tenant_id; enforced by row-level security | Cheapest, simple operations | Isolation depends on policy correctness | Example: Starter, Business |
Row-level security example for the pooled database (PostgreSQL syntax):
ALTER TABLE project ENABLE ROW LEVEL SECURITY;
ALTER TABLE project FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON project
USING (tenant_id = current_setting('app.tenant_id')::uuid)
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);
| Store | Isolation control | Second line of defence |
|---|
| Example: Primary database | Row-level security on every tenant table | Automated test that every table has a policy; application role cannot bypass RLS |
| Example: Cache | Keys prefixed t:{tenant_id}: | Cache wrapper refuses keys without prefix |
| Example: Object storage | Prefix per tenant; Enterprise bucket per tenant | Signed URLs scoped to prefix |
| Example: Search | Mandatory tenant filter added by query builder | Index alias per tenant for Enterprise |
| Example: Backups | Pool backups encrypted; Enterprise backups per tenant | Restore runbook per tenant tested quarterly |
| Example: Analytics copy | tenant_id retained; access by role | No cross-tenant data in customer-facing reports |
6. Noisy neighbour controls
| Control | Starter | Business | Enterprise | Behaviour when exceeded |
|---|
| API rate limit (token bucket) | Example: 20 req/s, burst 40 | 100 req/s, burst 200 | 500 req/s, burst 1,000 | HTTP 429 with Retry-After |
| Concurrent background jobs | 2 | 10 | 50 reserved | Jobs queue; fair scheduler |
| Storage quota | 5 GB | 200 GB | Contract | Uploads rejected; admin notified at 80% |
| Search queries per minute | 300 | 3,000 | 20,000 | 429; cached results served |
| Export size per job | 1 GB | 20 GB | 200 GB | Split into parts |
Fair scheduling: [describe]. Example: background workers pull from
per-tenant queues in weighted round-robin, so a tenant importing a
million rows cannot delay notifications for others. Database statement
timeout is 5 s for pooled tenants. Heavy tenants are detected when they
use more than 5% of pooled capacity for 15 minutes and are flagged for
review or tier change.
7. Tenant lifecycle
The state diagram shows the lifecycle of a tenant.
7.1 Onboarding
| Step | Action | Automated by | Target time |
|---|
| 1 | Example: Create tenant record, tier, region | Tenant service | < 1 s |
| 2 | Create identity provider organisation and admin user | Onboarding workflow | < 10 s |
| 3 | Pool: seed default data. Silo: provision database from template | Workflow; infrastructure as code for silo | Pool < 30 s; silo < 20 min |
| 4 | Apply tier quotas and feature flags | Configuration service | < 5 s |
| 5 | Send welcome email; register billing | Billing integration | < 1 min |
7.2 Offboarding
| Step | Action | Evidence |
|---|
| 1 | Example: Offer data export for 30 days | Export job ID |
| 2 | Disable logins and API keys | Audit log entry |
| 3 | Delete rows (pool) or drop database (silo) | Deletion job report |
| 4 | Delete object storage prefix or bucket; search index entries | Storage inventory diff |
| 5 | Backups expire under retention policy; Enterprise keys destroyed | Key destruction record |
| 6 | Issue deletion certificate to customer | Signed certificate |
8. Per-tenant configuration and feature flags
| Type | Example | Stored in | Changed by |
|---|
| Tier entitlement | Example: SSO, audit log export, API limits | Plan catalogue | Billing change only |
| Tenant setting | Example: Working days, logo, default language | Tenant settings table | Tenant admin |
| Release flag | Example: new-gantt-view on for 10% of tenants | Feature flag service | Product team |
| Operational override | Example: Raised rate limit during migration | Configuration service with expiry | SRE, with ticket |
Rules: flags are evaluated with tenant ID and tier as attributes; every
override has an owner and an expiry; entitlement checks run on the
server, never only in the client.
9. Security and compliance
| Topic | Design |
|---|
| Cross-tenant access testing | Example: Automated suite tries every endpoint with a token of tenant A and IDs of tenant B; runs in CI |
| Encryption | At rest for all stores; Enterprise may supply its own key for its silo |
| Data residency | Example: EU and US stacks; tenant region fixed at onboarding; pooled services deployed per region; no replication across regions |
| Administrative access | Support staff access a tenant only with a ticket and time-limited grant, logged and visible to the tenant admin |
| Audit logging | Per-tenant audit log of admin and data export actions; retention per tier |
| Certifications and contracts | [e.g. SOC 2 report, ISO/IEC 27001 certificate, DPA per tenant] |
| Sub-processors | Listed per region; tenants notified of changes |
10. Observability per tenant
| Signal | Tenant dimension | Notes |
|---|
| Logs | tenant_id field on every line | Searchable per tenant; masked personal data |
| Traces | tenant_id span attribute | Sampling raised for Enterprise |
| Metrics | Example: Tier label on all metrics; tenant_id label only on a small set of usage metrics | Avoids high-cardinality series |
| Usage meter | Requests, storage, job seconds per tenant per hour | Feeds cost allocation and billing |
| SLO reporting | Availability and latency per tier; per tenant for Enterprise | Monthly report to Enterprise tenants |
11. Cost allocation
The monthly cost attributed to tenant t is
Ct=Dt+Cpool⋅∑k=1Nukut+NCfixed where Dt is the cost of resources dedicated to the tenant,
Cpool is the cost of pooled resources, ut is the tenant's
usage units (for example weighted request and storage units), N is the
number of tenants and Cfixed is shared platform cost that
does not vary with usage.
Example for a Business tenant: Dt=500, Cpool=40,000,
ut=1,200 of ∑uk=20,000 units, Cfixed=12,000
and N=400:
Ct=500+40,000×0.06+30=500+2,400+30=2,930 USD.
At a price of USD 4,500 the gross margin is (4,500−2,930)/4,500≈34.9%.
| Tier | Tenants | Average Ct | Average price | Margin |
|---|
| Starter | Example: 300 | 45 | 300 | 85% |
| Business | 90 | 2,100 | 4,500 | 53% |
| Enterprise | 10 | 21,000 | 38,000 | 45% |
| [Tier] | [n] | [Cost] | [Price] | [%] |
12. Scaling
| Layer | Scaling approach | Trigger | Limit per unit |
|---|
| Application services | Example: Horizontal autoscaling | CPU > 60% or p95 > 300 ms | n/a |
| Pooled database | Example: Shards (cells) of up to 500 tenants each; tenant-to-cell map in registry | Cell at 70% CPU or 2 TB | 500 tenants or 2 TB |
| Siloed databases | One per Enterprise tenant; sized per contract | Tenant growth | Per tenant |
| Event broker | Partitions keyed by tenant ID | Consumer lag > 30 s | [n] partitions |
Cell placement: new tenants go to the least-loaded cell in their region.
A cell is a blast-radius boundary: an incident in one cell affects at
most its tenants.
13. Migration between tiers
| Step | Pool to silo (upgrade to Enterprise) | Rollback |
|---|
| 1 | Example: Provision dedicated database in tenant's region | Delete new database |
| 2 | Copy tenant rows using logical replication filtered by tenant_id | Stop replication |
| 3 | Verify counts and checksums per table | n/a |
| 4 | Put tenant in read-only mode (target < 15 min) | Re-enable writes on pool |
| 5 | Final sync; switch tenant-to-database mapping in registry | Switch mapping back |
| 6 | Re-enable writes; monitor for 24 h | Switch back within 24 h if needed |
| 7 | Delete tenant rows from pool after 14 days | n/a |
Downgrades (silo to pool) follow the same steps in reverse and are
allowed only at contract renewal.
14. Risks and open questions
| ID | Item | Type | Mitigation or next step | Owner |
|---|
| R-01 | Example: A new table is added without a row-level security policy | Risk | CI check fails if any table with tenant_id lacks a policy | [@name] |
| R-02 | Example: One very large Business tenant dominates a cell | Risk | Heavy-tenant detection; move to own cell | [@name] |
| OQ-01 | Example: Should Enterprise tenants get dedicated compute in addition to data? | Question | Decide after first three Enterprise contracts | [@name] |
| [ID] | [Item] | [Risk / Question] | [Action] | [@name] |
15. Appendices
15.1 References
| Reference | Use |
|---|
| [Cloud provider SaaS architecture guidance] | Silo, pool and bridge vocabulary |
| PostgreSQL documentation, row security policies | Section 5 example |
| [Internal ADR on tenancy model] | Section 3 decision |
15.2 Tenant registry fields
| Field | Description |
|---|
tenant_id | UUID, immutable |
tier | Starter, Business or Enterprise |
region | Residency region, fixed at onboarding |
cell_id | Pooled cell, or null for silo |
database_ref | Connection reference for silo tenants |
state | Lifecycle state from section 7 |
Standard and licence note
Based on common SaaS architecture practice for multi-tenant systems,
including the widely used silo, pool and bridge tenancy models. Original
text written for this collection; examples, limits and figures are
illustrative. Template licence: MIT.