SaaSGuide
daLæs på danskMulti-Tenant SaaS Architecture: One Database or Many?
Multi-tenant SaaS architecture compared: shared database, schema per tenant or database per tenant. The real trade-offs, and which model I pick and why.

Freelance full-stack developer
- Published
- Reading time
- 12 min
In this post9
Multi-tenant SaaS architecture means a single running copy of your application serves every customer, while each customer's data stays fenced off from everyone else's. There are three ways to build that fence at the database level: one shared database with a tenant ID on every row, a separate schema per tenant, or a separate database per tenant. For most new B2B products I recommend the shared database, and I'd only move a customer into its own database when a contract or a regulator makes the extra operations work worth it.
This choice is expensive to reverse, which is why it's worth understanding even if you'll never write a line of the code yourself.
The short answer
| Shared database (pool) | Schema per tenant (bridge) | Database per tenant (silo) | |
|---|---|---|---|
| How data is separated | A tenant_id column on every row | Each tenant gets its own set of tables in a separate schema | Each tenant gets a whole database |
| Isolation | Low: the app must filter correctly every time | Medium | High |
| Running cost | Lowest | Low to medium | Highest |
| Schema migrations | Run once | Run once per tenant | Run once per tenant |
| Restore one tenant from backup | Painful | Doable | Easy |
| Per-tenant customization | Hard | Possible | Possible |
| Typical fit | Most new SaaS products | PostgreSQL apps with a moderate number of tenants | Enterprise or regulated customers who demand separation |
The decision rule I use: go with a shared database unless a customer, a contract or a law requires physical separation today, not in some imagined future. If they do, give those specific customers their own database and keep everyone else pooled.
Tenancy is one decision among many when you build a product. If you want the bigger picture first, start with my complete guide to building a SaaS.
Tenants, users and where the wall goes
A tenant is whoever pays for the account and owns the data in it. In B2B SaaS that's almost always a company, an organization or a team, not an individual user. A project management tool might have agencies as tenants, each with a few dozen people logging in.
That distinction tells you where the hard boundary sits. People inside the same company can see each other's data, depending on their roles. People from two different companies never can. Roles handle the first problem, and I've covered that in designing SaaS roles and permissions. Tenancy handles the second.
The opposite of multi-tenant is single-tenant: every customer gets their own deployment of the whole application, often on dedicated infrastructure. That gives you maximum isolation, but every release has to be rolled out to every customer separately. It's rarely right for a standard product, though it has its place, as you'll see below.
All three models are about the data layer. The application code is the same for every tenant in each of them.
Shared database: one set of tables, tenant_id everywhere
Every customer lives in the same tables. Each row carries a column, usually tenant_id, that says who owns it. When a user signs in, the app resolves which tenant they belong to and scopes every query to that ID.
The upside is operational simplicity. One database to back up, monitor and upgrade. Each migration (a change to the database structure) runs once. Cross-tenant reporting, like how many customers adopted a new feature last month, is a normal query. Onboarding a new customer costs close to nothing, because it's just a new row.
The downside is that isolation is only as good as your code. Forget the filter in one place and Customer A sees Customer B's invoices. Microsoft is blunt about this in its overview of SaaS tenancy models: a database holding many tenants gives up some isolation by design. You also get the noisy neighbor problem, where one heavy customer running big exports slows things down for everyone else.
Keeping tenant data from leaking
In Laravel, the natural home for the tenant filter is a global scope, a constraint that's added automatically to every query on a model. That way nobody has to remember it. A global scope doesn't cover everything, though, and these are the places where leaks usually slip through:
- Raw SQL and queries that bypass the models.
- Queued jobs, where there's no signed-in user to tell the code which tenant it's working for.
- Cache keys, uploaded files and search indexes, which need tenant separation too.
- Unique constraints. An invoice number or an email address usually has to be unique per tenant, not across the whole database, so the index needs to include
tenant_id.
On PostgreSQL you can add a second safety net inside the database itself with row-level security. The database then refuses to return another tenant's rows even if the application has a bug. One catch: table owners bypass these policies by default, so the app should connect as a database role that doesn't own the tables.
Schema per tenant: a PostgreSQL middle ground
A schema is a named group of tables inside a database. In this model each tenant gets its own copy of every table in its own schema, but all the schemas live in one database on one server. The code doesn't need to filter by tenant, because it can only see the tables in the schema it's pointed at.
That sounds like the best of both models, and for some products it is. There are two caveats.
First, in practice this is mostly a PostgreSQL pattern. In MySQL, CREATE SCHEMA is a synonym for CREATE DATABASE, so schema per tenant there is simply database per tenant under another name. Your database engine and your tenancy model are linked decisions, so make them together when you choose a SaaS tech stack.
Second, every migration now runs once per schema. With 500 tenants, a release means 500 migrations, and if number 312 fails you have customers on two versions of your database at the same time. Thousands of schemas in a single database also make backups and maintenance heavier. And if you use connection pooling (a layer that shares database connections between requests), switching schemas safely on every request needs care.
I rarely pick this model. It fits best when you're on PostgreSQL, have a moderate number of tenants and a genuine reason to keep tables apart, such as some customers needing extra fields.
Database per tenant: maximum isolation, maximum operations
Each tenant gets its own database. A central database keeps track of which tenants exist, which plan they're on and where their database lives. When a request comes in, the app switches its connection to that tenant's database.
This gives you the strongest separation short of a separate deployment per customer, plus a few benefits that are hard to get any other way:
- You can restore one tenant from backup without touching anyone else. If a customer deletes something important by mistake, it's a contained job.
- A large tenant can be moved to its own server if it starts crowding out the rest.
- When a customer leaves and asks for erasure, you drop their database.
- You can place a tenant's database in a specific region, for example keeping a German customer's data in a Frankfurt data center if their contract requires it.
That last point deserves attention if you sell in Europe. Larger companies and public sector buyers in the EU, UK and the Nordics often send security questionnaires asking exactly how tenant data is separated and where it's stored. A dedicated database is the easiest answer to give, though a well-built shared database is often acceptable too.
The price is operations. Every migration runs per tenant, every database needs backups and monitoring, and your connection count grows with your customer list. Cross-tenant reporting, such as an overview of active accounts, now means pulling data into the central database or a separate analytics tool. On managed cloud databases that bill per instance, the hosting bill can climb quickly.
In Laravel I wouldn't build this from scratch. Tenancy for Laravel supports database-per-tenant setups, creates the database automatically when a tenant is created, and ships a command that runs migrations across all tenants. It also supports schema per tenant on PostgreSQL and a single shared database, so you're not locked in from day one.
Which model I pick, and when I'd skip multi-tenancy
For nearly every new SaaS I recommend starting with a shared database. Most products need to reach their first paying customers fast, and the shared model is the cheapest to build, run and change while you're still learning what customers want. What else belongs in that first version is a separate question, covered in my guide to scoping a SaaS MVP.
I'd recommend database per tenant from the start when at least one of these is true:
- You're selling to a handful of large customers who write data separation into the contract.
- You're in healthcare, finance or the public sector, and buyers ask about isolation in their procurement or security reviews.
- One customer is likely to hold more data than all the others combined.
- Customers need their data in a specific country or with a specific hosting provider.
Over time, the setup I most often recommend is a hybrid: a shared database for most tenants and dedicated databases for the few who pay for it. Microsoft describes the same pattern, with trial tenants sharing a database and premium tenants getting their own. It only works if the code uses tenant_id everywhere, including inside the dedicated databases, so the exact same code runs in both places.
And sometimes you shouldn't build multi-tenancy at all. If the software is for one company, it's an internal tool, not a SaaS. If you have three large customers who each want their own heavily customized version, separate deployments may be more honest than forcing them into one product. And if you need to serve thousands of enterprise tenants across several regions with certifications like ISO 27001 from day one, you need a team with dedicated operations people, not just a freelance developer like me.
Building it in from day one: 7 steps
Whichever model you choose, the same steps decide whether multi-tenancy becomes a strength or a steady source of bugs.
- Define what a tenant is. A company, a department, a team? Can one user belong to several tenants, like a consultant working for many clients? The answer shapes the whole data model, so write it down before anyone writes code.
- Find out what isolation your customers expect. Ask your first customers, or read the contracts and tenders you want to win. Data processing agreements, data location and deletion all belong here, and my GDPR checklist for SaaS covers the technical side.
- Choose a model and record why. One page is enough. The next developer can then see why the decision was made and when it should be revisited.
- Add tenant_id to every table from the start. Even if you choose database per tenant. It costs almost nothing now and lets you move tenants between models later.
- Make the tenant part of everything, not just the database. Queued jobs, cache, file storage, search, outgoing email, logs and error reports all need to know the current tenant.
- Test isolation automatically. I recommend creating two tenants in your test suite and asserting that Tenant B can never read, change or export Tenant A's data. Run it on every change, not just before launch.
- Plan per-tenant backup and restore. Practice restoring one customer's data before a customer asks you to. My notes on a backup strategy for web apps cover how to set it up so restores actually work.
Before your first paying customer signs up
- Every table holding customer data has a tenant ID, and queries are scoped automatically.
- Unique indexes and number sequences, such as invoice numbers, are unique per tenant.
- Queued jobs, cache, file storage and search indexes are separated by tenant.
- Admin tools that can see across tenants are limited to a few people, and their use is logged.
- An automated test proves that one tenant can't see another tenant's data.
- You've rehearsed restoring and deleting a single tenant's data.
- The chosen model and the reasoning behind it are written down.
Next steps
If you're planning a SaaS, make the tenancy decision together with your choice of database and hosting, and before your first customer account exists. If you already have a product where customers share tables without a tenant ID, it can still be fixed. It just needs a migration plan that moves data without downtime.
To see how I run projects like this, from a fixed-price discovery phase through to production, have a look at my SaaS development service.
Frequently asked questions
Can I move from a shared database to database per tenant later?
Yes, but it gets more expensive the longer you wait. If every table has a tenant ID from the start, moving one customer into its own database is manageable: copy that tenant's rows across and point their connection at the new database. Without tenant IDs you first have to work out which data belongs to whom, and that is usually the biggest part of the job.
Does GDPR require a separate database for each customer?
No. Article 32 of the GDPR requires security measures that are appropriate to the risk, but it doesn't prescribe a database model. A shared database with solid access control and tests can meet that standard. Your customers may still demand stricter separation in their contracts, larger EU companies in particular. This isn't legal advice, so check with a lawyer if a lot is riding on it.
How does the app know which tenant a request belongs to?
Usually from the login or the URL. The simplest approach links each user to a tenant and looks it up when they sign in. Many SaaS products also give each tenant a subdomain, like acme.yourapp.com, or let larger customers use their own domain. If one user can belong to several tenants, you need a clear way to switch between them.
How many tenants can a shared database handle?
Far more than most SaaS products ever reach, if it's designed properly. The limit is rarely the number of tenants. It's whether your indexes start with the tenant ID and whether a few customers run very heavy queries. If one tenant starts hurting performance for the rest, you can move just that tenant to its own database, provided the code was prepared for it.