Designing multi-tenant SaaS architecture: tenancy models and trade-offs
A practical guide to choosing tenant boundaries, protecting data and keeping a SaaS platform operable as it grows.
5 min read · 1,083 words
In this article
Start with the boundary, not the database
Multi-tenancy means one product serves several customer organisations while keeping each organisation's data and access separate. The difficult part is rarely adding a tenant identifier to a table. The real work is deciding where the boundary lives, how consistently the application enforces it, and what operators can see when something goes wrong.
A useful design starts with business constraints. Ask whether tenants need different retention rules, regions, encryption keys, release schedules or contractual isolation. Estimate how uneven their workloads may become. Decide whether support staff need controlled cross-tenant access. These answers shape the architecture more reliably than choosing a popular tenancy pattern first.
Write the tenant context down as part of every request path. A user signs in, the system resolves the organisation they can act within, and all later authorization and data access derives from that trusted context. Client-supplied tenant identifiers are hints for routing or interface state, never proof of access.
Three common data models
The shared-table model stores many tenants in the same tables and adds a tenant key to tenant-owned rows. It is economical and easy to operate at moderate scale. Schema changes happen once, connection pools stay efficient, and aggregate maintenance is straightforward. Its cost is discipline: every query, unique constraint, cache key and background job must include tenant scope.
Schema-per-tenant keeps tenants in separate schemas inside a database. It creates a clearer logical boundary and can make tenant export or targeted maintenance easier. It also multiplies migration work. Tooling must track schema versions, recover partial migrations and prevent connection search paths from leaking between requests.
Database-per-tenant provides the strongest operational boundary of the three. It can support regional placement, separate backup policies and moving a demanding tenant to dedicated capacity. It also increases infrastructure, connection and observability overhead. Thousands of mostly idle databases can be less practical than a smaller shared system.
These choices do not have to be permanent or universal. A shared default with an escape path for regulated or high-volume tenants is often a sensible design. The important thing is to keep tenant placement behind a repository or routing boundary so business logic does not need to know where records physically live.
Make isolation hard to forget
Isolation should be a property of the data-access layer, not a convention each feature author remembers. In Laravel, global scopes can help, but they need explicit tests and careful handling in administrative jobs. In PostgreSQL, row-level security can add a database-enforced boundary. Neither removes the need for application authorization; they provide complementary layers.
Model uniqueness within a tenant. An invoice number, project slug or member email may only need to be unique for one organisation, so the database constraint should combine that value with the tenant key. Apply the same rule to cache keys, object-storage prefixes, search indexes, queue payloads and idempotency records.
Cross-tenant administration deserves its own path. Support access should be time-bound where practical, logged, and clear in the interface. Avoid a hidden switch that silently turns an ordinary session into a global one. An operator should always know which tenant context is active.
Identity and authorization are different jobs
Authentication establishes who a person is. Authorization decides what that person may do inside a tenant. Treat organisation membership as a first-class record with a role or policy assignment. This supports one person belonging to more than one organisation without duplicating their identity.
Resolve permissions on the server for every protected action. A role label such as administrator is often too broad once a product matures. Policies expressed around capabilities—manage billing, invite members, export records—are easier to audit and change. Keep sensitive operations explicit even if most screens rely on a common permission middleware.
Invitations, membership removal and ownership transfer need deliberate lifecycle rules. Decide what happens to active sessions when access is revoked. Decide who can remove the final owner. These are product decisions with security consequences, and they are cheaper to settle before customers depend on accidental behaviour.
Background work must carry context
Queues, scheduled tasks and webhooks run outside the original request, so tenant context does not arrive automatically. Include a trusted tenant identifier in each job payload and restore the correct data boundary before work begins. Reject jobs whose tenant no longer exists or whose referenced record belongs elsewhere.
Recurring tasks should iterate tenants in bounded batches rather than loading every organisation at once. Noisy tenants may need separate queues or rate limits. Usage metering should record immutable events with tenant ownership so billing can be recalculated and investigated without relying on a mutable counter.
Observability also needs tenant awareness without exposing customer data. Logs and traces can include an internal tenant identifier, request identifier and deployment version. Dashboards can show latency or failure rates by tenant tier while access controls limit who may inspect a specific organisation.
Plan for movement and recovery
A tenant boundary becomes more valuable when it supports operational change. Define how to export one tenant, restore one tenant after an accidental deletion, and move one tenant between placements. Shared databases make a complete restore easy but a tenant-level restore more involved. Dedicated databases invert that trade-off.
Backups are only useful after a restore has been exercised. Test both broad disaster recovery and targeted recovery. Record which object-storage files, search documents and external-provider records belong to the tenant, because restoring relational rows alone may leave the account inconsistent.
Schema evolution needs the same care. Prefer backward-compatible changes that allow old and new application versions to run during deployment. For tenant-specific stores, migrations should be resumable and expose progress. A failed migration needs a visible state and a safe retry path.
Choose with evidence
Begin with the simplest model that meets the known isolation and operational requirements, then preserve an escape route. A shared-table design can be robust when tenant context is enforced centrally and tested at every boundary. A database-per-tenant design can still be unsafe if credentials, caches or operator tools ignore the separation.
Test the failure modes directly. Attempt cross-tenant record access, guessed identifiers, stale queue jobs, cache collisions and revoked memberships. Review queries that intentionally cross tenant boundaries. Measure the cost of large tenants instead of assuming all organisations behave alike.
The strongest multi-tenant architecture is understandable to the people operating it. Its boundary appears in authorization, data access, jobs, storage, observability and recovery. When those parts agree, the platform can grow without turning every new feature into another isolation risk.
Written by
Divesh Chakrayat
Founder and lead engineer
Divesh writes from hands-on work across SaaS products, backend systems, integrations and production delivery.
Related insights
API integration done right: idempotency, retries and webhooks
How to design API integrations that survive duplicate requests, temporary failures, delayed webhooks and operational surprises.
Modernizing a legacy PHP application without a full rewrite
A staged approach to improving a legacy PHP system while preserving working behaviour and keeping delivery moving.
Need to apply this thinking to your system?
Bring the constraints you are working with. We will help turn them into a practical engineering path.