How to create a multi-tenant database with shared table structures?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
A shared-table multi-tenant database stores many tenants in the same physical tables and separates them logically with a tenant key. It is one of the cheapest and simplest SaaS data models, but it only works well if tenant isolation is built into every table, query, index, and permission rule.
The Core Model
In the shared-table approach, tenant-specific rows live side by side:
- one
customerstable for all tenants - one
orderstable for all tenants - one
userstable for all tenants
The required design rule is simple:
- every tenant-owned row carries a tenant identifier
Without that, you do not have multi-tenancy. You just have a data leak waiting to happen.
Start With the Tenant Key
The most important column is usually something like:
- '
tenant_id' - '
account_id' - '
organization_id'
It should appear in every table that stores tenant-scoped data.
A practical schema might look like this:
Notice the pattern: composite keys include tenant_id. That prevents accidental cross-tenant joins.
Why Composite Keys Help
A lot of shared-table designs fail because tenant_id exists but is treated as an optional filter rather than part of identity.
If customer_id is globally unique, developers may get lazy and stop including tenant_id in joins. That makes future mistakes easier. Composite keys force the tenant boundary into the schema itself.
That is not the only valid approach, but it is one of the safest.
Index for Tenant-Scoped Queries
Most application queries are tenant-scoped, so indexes should reflect that.
Putting tenant_id first helps the database prune work quickly when the application asks for one tenant's rows.
Enforce Isolation in the Application Layer
The application should never run a tenant-scoped query without a tenant filter.
Bad:
Better:
That sounds obvious, but accidental missing tenant filters are the biggest risk in shared-table systems.
Database-Level Protection Is Better Than Hope
If your database supports row-level security, use it. Application checks are necessary, but defense in depth is better.
PostgreSQL example:
Then the application sets the tenant context on the session before running queries. This does not remove the need for correct query design, but it gives you another guardrail.
Uniqueness Must Usually Be Tenant-Scoped
Many business rules should be unique per tenant, not globally unique.
For example, customer email may need to be unique within one tenant but allowed in another tenant.
That is a subtle but important part of schema design. Global uniqueness often creates the wrong constraints in SaaS systems.
When Shared Tables Are a Good Fit
This model is strongest when:
- tenants are small to medium
- workloads are similar
- cost matters
- operational simplicity matters
- cross-tenant analytics are useful
It is weaker when:
- one tenant is extremely large
- data residency rules differ by tenant
- per-tenant restore is critical
- noisy-neighbor isolation is strict
At that point, separate schemas or separate databases may be a better architecture.
Migration and Operations
A nice property of shared tables is that schema migrations happen once for everybody. That simplifies rollout, but it also means one bad migration affects every tenant.
Operationally, you should plan for:
- tenant-aware backups
- tenant-scoped export tools
- per-tenant rate limiting
- query monitoring by tenant
Those become important long before you outgrow the schema itself.
Common Pitfalls
- Forgetting
tenant_idon one table or one join path. - Using globally unique IDs and then omitting tenant filters by accident.
- Building indexes without leading tenant columns, which hurts tenant-scoped query performance.
- Relying only on application discipline instead of adding database-level guardrails.
- Choosing shared tables even when a few very large tenants will dominate the system.
Summary
- Shared-table multi-tenancy means all tenants share the same tables but each row carries a tenant key.
- Put
tenant_idinto every tenant-owned table and into key relationships. - Design indexes and uniqueness constraints with tenant scope in mind.
- Use database protections such as row-level security where available.
- Shared tables are efficient, but they only stay safe if isolation is treated as a schema rule, not a coding convention.
Related reading
- How to create a MySQL hierarchical recursive query?
- How to Create a nested index in MongoDB?
- How to create an Index in Amazon Redshift
- How to Create and Use Enum in Mongoose
- How to create arguments for a Dapper query dynamically
- How to create indexes in MongoDB via .NET
- How to create liquibase changeset for integration tests in springboot?
- How to create postgis extension for postgresql in docker?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.