Tenancy models: shared schema, separate schema, separate database
coreintermediateA multi-tenant system has to decide, per resource, how much of the database each tenant shares with every other tenant. Shared database/shared schema puts every tenant's rows in the same tables, distinguished only by a tenant_id column. Shared database/separate schema still uses one database instance but gives each tenant its own schema (its own set of tables inside that instance). Separate database gives each tenant an entirely distinct database, sometimes on its own host. Each step trades lower operational cost for weaker isolation, so the right choice depends on what a single tenant's data leak or one tenant's heavy load is allowed to cost the others.
Think of it as
Think of three ways to run a shared office building. Shared schema is an open-plan floor: everyone's desks are in the same room, separated only by a nameplate — cheap to build, but a wrong turn puts you at someone else's desk. Separate schema is individual offices on the same floor sharing one reception desk and one electrical system: real walls between tenants, but a building-wide power outage or a receptionist's mistake still affects everyone. Separate database is each tenant getting its own building: nothing physically shared at all, at the cost of running many buildings instead of one.
What we're doing: Compare how the same "list this tenant's open tickets" query is scoped under each of the three models.
- 3
- The WHERE tenant_id filter is the entire isolation boundary here — omit it once, in one query, and every tenant's tickets return together.
- 8
- The schema switch scopes the query without a tenant_id column, but the connection pool, CPU, and disk I/O underneath are still shared with every other tenant's schema.
- 15
- By the time the query runs, tenant scoping already happened at connection time — there is no shared table left for a missing filter to expose.
Why this works: The same logical query needs a different amount of application trust at each step: shared schema trusts every query author to remember the filter, separate schema trusts the connection-time schema switch, and separate database needs no per-query trust at all because there is nothing shared left to mis-scope.
Choosing shared schema for a tenant with a hard compliance requirement for data separation
Wrong
Better
What you see: A compliance audit or customer security questionnaire asks to prove that tenant data is physically separated, and the honest answer is "it is not — isolation depends on every one of several thousand queries carrying a correct tenant_id filter," which is not an answer regulators or enterprise security teams accept.
Why: Isolation strength is a property of the architecture, not of how careful the current engineering team is — a compliance requirement for data separation needs a model where a code review mistake cannot cross a tenant boundary, which shared schema structurally cannot guarantee no matter how disciplined the team is.
- Shared schema (weak isolation, low cost)
- One tenants table, one tickets table, one everything table
- Only a WHERE tenant_id = ? clause keeps rows apart
- One connection pool and one migration for every tenant at once
- Separate database (strong isolation, high cost)
- Each tenant's tables live in a database only that tenant's connection can reach
- A missing WHERE clause cannot leak another tenant's rows — there is nothing to leak into
- N databases to provision, back up, migrate, and monitor instead of one
Three tenancy models compared
Together
Remember: Shared schema is cheapest but leans entirely on application code getting every tenant_id filter right; separate database is the only model where a mistake structurally cannot cross tenant boundaries. Isolation strength and operational cost move in opposite directions, and the choice can be made per tenant, not just once for the whole product.
See also: isolation at every access path · noisy neighbor and quotas in multi tenant systems

