Multi-Tenant Application Design

A multi-tenant application serves many customer organizations — tenants — from one codebase and, usually, shared infrastructure. Each tenant sees only its own users, data, and settings, as if the product were theirs alone. Almost every business SaaS product works this way, because running one deployment for thousands of customers is far cheaper and simpler than running thousands of deployments.

The price of sharing is risk. In a multi-tenant system, a single missing WHERE tenant_id = ?, a cache key without a tenant prefix, or a background job that runs in the wrong context can show one customer's data to another — the incident B2B customers forgive least. Good multi-tenant design chooses the right level of isolation for each kind of customer and makes tenant context so pervasive that forgetting it is difficult.

TL;DR

Quick Example

Shared tables with PostgreSQL row-level security. The application sets the tenant for each transaction, and the database refuses to return or write another tenant's rows, even if a query forgets its filter.

If code runs a query without setting a tenant, current_setting fails and the query errors instead of returning everyone's data — the isolation fails closed. The application must connect as a role without BYPASSRLS or superuser rights, or the policies are silently skipped. See Row-Level Security.

Core Concepts

Tenancy Models

Many products are hybrid: most tenants share a pool, while enterprise tenants pay for a silo. Building the application so the same code runs against either keeps that option open.

Tenant Context

Every request, job, and event must know which tenant it acts for. Resolve the tenant at the edge from trusted inputs — the authenticated session or token claims, checked against the user's memberships, or a verified custom domain — and carry it in request-scoped context (AsyncLocalStorage in Node.js, contextvars in Python, request scope in Java frameworks). Pass it explicitly into the data layer rather than letting each query rediscover it.

Users often belong to several tenants. Model membership explicitly (memberships(user_id, tenant_id, role)), let the user choose the active tenant, and scope sessions or tokens to one tenant at a time. Authorization within a tenant is covered in Authorization Models: RBAC, ABAC, and ReBAC.

Layers of Isolation

Enforcing Isolation in the Data Layer

Scoped Access by Default

Make the tenant-scoped path the easy one: repositories or ORM scopes that require a tenant, query helpers that add the filter automatically, and a deliberately awkward, audited "system" context for the rare cross-tenant job. Row-level security then acts as a backstop for the query someone writes by hand.

Tenant IDs Everywhere

Put tenant_id on every tenant-owned table, including child tables whose parent already has one. It makes policies simple, indexes efficient, and future sharding by tenant possible. Lead indexes and unique constraints with it — UNIQUE (tenant_id, name), not UNIQUE (name), so two tenants can each have a project called "Website".

Prevent Cross-Tenant References

A foreign key on project_id alone lets a task in tenant A point at a project in tenant B. Including the tenant in the key makes that impossible:

Identifiers

Globally unique identifiers, such as UUIDs, avoid revealing how many records other tenants have, make URLs harder to guess, and let you move a tenant's data to another database without renumbering it.

Tenant-Aware Everything Else

Data leaks between tenants rarely come from the main tables. They come from everything around them:

Noisy Neighbors and Fairness

In a shared system, one tenant's bulk import, runaway integration, or enormous report can slow everyone else. Protect tenants from each other:

Proving these protections work needs dedicated tests — see Tenant Isolation and Noisy Neighbor Testing.

The Tenant Lifecycle

Best Practices

Fail Closed

Missing tenant context should cause an error, never an unfiltered query. Design defaults — RLS that errors without a setting, repositories that demand a tenant — so mistakes are loud.

Test Isolation Like a Feature

Run automated tests that act as one tenant and try to read, update, and enumerate another tenant's resources across every endpoint. See Authorization Testing Across Tenant Boundaries.

Audit Cross-Tenant Access

Support staff and internal tools sometimes need access across tenants. Make it explicit, time-limited, approved where appropriate, and logged — and visible to the customer if your contracts promise that.

Plan for Sharding Early

A tenant_id on every table and tenant-scoped queries make it straightforward to split tenants across databases later. See Database Sharding.

Common Mistakes

Trusting the Tenant ID From the Request

Filtering Only in Handlers

Connecting as a Role That Bypasses RLS

Cache Keys Without a Tenant

FAQ

Which tenancy model should I start with?

For most new SaaS products, shared tables with a tenant_id on every row, scoped data access, and row-level security. It is the cheapest to run and simplest to migrate. Keep the code independent of where a tenant's data lives so that a dedicated-database tier can be added for customers who need it.

Is row-level security enough on its own?

It is a strong backstop, not a complete design. Use it alongside tenant-scoped application code, tenant-aware caches, jobs, and storage, and automated cross-tenant tests. RLS protects the database; it cannot stop a cache or a file URL from leaking data.

How do users who belong to several tenants work?

Store memberships explicitly with a role per tenant, let the user pick the active tenant, and scope each session or token to that tenant. Switching tenants should re-run authorization rather than reuse permissions from the previous tenant.

When should a tenant get its own database?

When contracts or regulation require physical separation, data must live in a specific region, the tenant's size or workload affects others, or the customer needs independent backups, restores, or encryption keys. Price the tier to cover the extra operational cost.

How do we run migrations across many tenant databases?

Automate them, run them in batches with monitoring, track the schema version per tenant, and keep every migration backward compatible so the application works while the fleet is partly migrated. See Database Migrations.

Related Topics

References