Multi-tenant: one database or one per customer?
What does multi-tenant mean?
A multi-tenant application serves many customer companies from one installation. Each customer is a tenant. Some products add a second level — a tenant that owns several companies — which matters if your customers are groups with subsidiaries. The architecture question is how hard the wall between tenants should be.
What are the common tenancy models?
There are three, and each is a trade between isolation and running cost:
- Database per tenant: the strongest isolation, a simple per-customer backup and restore, and no noisy neighbours. The cost is operational — every migration runs once per customer, connections multiply, and reporting across customers needs extra plumbing.
- Schema per tenant: a middle path. Isolation is good, but migrations still run per tenant and databases hit practical limits as the number of schemas grows.
- Shared tables with a tenant ID on every row: the cheapest to run and the easiest to migrate, because there is one schema. The risk is human — one query that forgets the tenant filter leaks data between customers.
How do you make row-level tenancy safe?
Treat the tenant filter as infrastructure, not as something each query remembers. Put the tenant ID on every row, resolve the tenant from the authenticated request in one middleware, and apply the filter in one shared data layer so that a feature developer cannot forget it. If your database supports row-level security, consider it as a second line of defence.
Then test the wall directly: create two tenants, write data as one, and assert that the other cannot read, update or delete it through any endpoint. Isolation bugs are the kind that only show up in production unless a test is looking for them.
Permissions are a separate axis
Isolation answers 'whose data is this?'. Permissions answer 'what may this person do with it?'. Model them separately: roles, and for each role a view, create, edit and delete flag per resource. On top of that, a per-tenant switch for which products or modules a customer has access to — with optional start and end dates — lets you sell, trial and pause without deploying anything.
What we chose, and the trade-off
For our own business apps we use shared tables. Every record carries a tenant ID, and a company ID where the data belongs to one company; shared services and middleware enforce it, and each service has its own database. Admins can add custom fields and extra modules per tenant and switch products on per tenant.
The trade-off is that this is row-level separation, not a database per customer. For many small and mid-size customers on one sign-in that is the right cost; for a customer who needs physical isolation, a dedicated deployment is the better answer.
How should you choose for your product?
A rule of thumb that has held up:
- Many small customers on one product: shared tables, with the tenant filter built into the data layer.
- A few large or regulated customers: a database per tenant.
- Both: shared tables by default and a dedicated database for the customers who need it — and design the tenant resolver so either works.
Decide it early
Whichever you pick, decide it at the start. Retrofitting tenancy into an application that assumed a single customer is one of the most expensive rewrites in software.