Guides · Architecture
Single-tenant vs multi-tenant: five ways to isolate SaaS customers
From a shared tenant_id column to an app instance per customer: what each model shares, what goes wrong, and when to move to the next one.
By Jonathan Pitter ·
The short answer
Multi-tenant means your customers share one running copy of your application, and usually one database, and your code keeps them apart. Single-tenant means each customer gets a copy of their own: their own application instance, their own database, often their own subdomain.
Multi-tenant is cheaper to run and simpler to upgrade. Single-tenant isolates customers properly, removes noisy neighbours and is often what an enterprise contract asks for, but every customer is another environment to provision, patch and monitor.
Most SaaS products don't sit at either extreme. There are five common models between "everyone in one table" and "everyone on their own servers", and the real decision is which one you can afford to operate.
Single-tenant vs multi-tenant at a glance
| Multi-tenant | Single-tenant | |
|---|---|---|
| What customers share | App, compute, database, often tables | Nothing at the app or data layer |
| How data is kept apart | A tenant_id filter, schemas or row-level security | Separate databases and processes |
| Noisy neighbours | One heavy customer slows everyone | One customer's load stays in their environment |
| Cost per customer | Low, and falls as you grow | Higher: every customer has a baseline footprint |
| Onboarding a customer | Insert a row | Provision an environment |
| Shipping an upgrade | Deploy once | Roll out to every instance |
| Per-customer config, versions, regions | Hard | Natural |
| Enterprise and regulated buyers | Often need extra assurances | Usually satisfied by the architecture |
| Worst failure | One bug leaks data across customers | One customer's environment goes down |
The five isolation models
Isolation is a spectrum. Each step down this list removes one thing customers share, and adds one thing you have to automate. AWS's SaaS guidance names the same ideas pool (everything shared), bridge (some layers shared) and silo (nothing shared), so you'll see those terms too.
Level 1: shared database, shared schema (pool)
Everyone's data lives in the same tables, separated by a tenant_id column. It is the usual starting point, and for good reason: one database, one migration, one deploy.
-- Every query needs this.
SELECT * FROM orders WHERE id = $1 AND tenant_id = $2;
-- Forget the filter once and you have a cross-tenant data leak.
SELECT * FROM orders WHERE id = $1;
The risk is that isolation is only as good as every query anyone ever writes. An ORM scope or PostgreSQL row-level security helps, but they are safety nets, not walls: PostgreSQL's CVE-2024-10976 (fixed in November 2024) let row-level security apply the wrong role's policies in some reused queries. And because everyone shares compute and storage, one customer's bulk import is everyone's slow afternoon.
Level 2: shared database, a schema per tenant (bridge)
Each tenant gets their own schema (tenant_a.orders, tenant_b.orders) inside one database. A missing WHERE clause no longer leaks data, but every migration now runs once per schema. At 10 tenants that is a loop; at 500 it is a migration orchestration system. Tenants still share the database server, so noisy neighbours remain.
This model takes on the operational cost of many databases without the isolation of separate ones, which is why many teams skip it.
Level 3: a database per tenant (bridge)
Each tenant gets their own database. Backups and restores happen per customer, a runaway query only hurts its owner, and "delete this customer's data" is one command.
You now run migrations across N databases, so schema drift becomes a real risk and your deploy pipeline has to know which tenant is on which version. And the application layer is still shared: one process, one memory space, one deploy for everyone.
Level 4: an app instance per customer (silo)
Each customer gets their own running copy of the application and their own database, separated at the infrastructure level: a container group, a VM, or a Kubernetes namespace with network policies and resource quotas. Compute, storage and networking are all isolated.
This is the model people mean by "one instance per customer", and it is architecturally clean but operationally heavy. For every customer you need to create the environment, deploy the app, route a hostname to it, issue a TLS certificate, provision a database and inject its credentials, set resource limits, lock down network traffic and wire up health checks. Then you need to do it again for each release.
Teams that run this by hand usually end up building an internal control plane, and that control plane becomes a second product to maintain.
Level 5: dedicated infrastructure per customer (silo)
The customer gets machines, a cluster or a cloud account of their own, sometimes in a region or VPC they choose. Nothing is shared, not even the kernel or the control plane. Government, healthcare and financial buyers sometimes require it by contract.
It is the most expensive model and the slowest to provision, and it only makes sense when a customer is paying for it explicitly.
How to choose
Start multi-tenant (level 1 or 3) when you sell self-serve, your customers are small, and nobody is asking where their data lives. Pooled infrastructure keeps your costs low while you find out what people will pay for.
Move to an instance per customer (level 4) when:
- an enterprise prospect's security review asks for dedicated environments, or asks how you prevent cross-tenant access and you don't like your answer;
- one customer's workload regularly degrades everyone else's;
- customers need their own configuration, release timing, custom domain or data residency;
- a single data leak would end the business.
Use dedicated infrastructure (level 5) only when a contract names it and pays for it.
Do both. Many SaaS products keep a pooled tier for self-serve customers and give enterprise customers a silo. The application code is the same; only where it runs, and whose data sits next to it, changes.
What running an instance per customer takes
If you're building level 4 yourself, this is the list of things each customer environment needs, and every one of them has to be automated, because you will do it hundreds of times:
- An isolation boundary: a namespace, project or VM, with access control, so one environment can't reach into another.
- Resource limits, so a busy customer can't starve the node.
- Network policies that only allow the traffic the app needs.
- The application itself: an image built from your repository and deployed with the customer's environment variables.
- A database created alongside it, with its credentials injected rather than copied around.
- A hostname and TLS certificate, issued and renewed automatically.
- Health checks, so a broken deploy is noticed before the customer notices it.
- Upgrades and rollbacks across every instance, ideally one customer at a time.
- Logs and deploy history per customer, for when one of them says "it's broken".
The work isn't any single item. It's keeping all nine consistent across every customer, every release.
Database per tenant vs a shared database
The database is where isolation matters most, and you can isolate it without isolating anything else.
- Shared database: cheapest and simplest. Isolation depends on application code and, ideally, row-level security. Per-customer backup and restore is painful.
- Database per tenant: strong data isolation, per-customer backups, easy deletion and simple data-residency answers. The costs are connection counts, many migrations and version drift.
A database per tenant inside a shared application (level 3) is a common halfway point. If you're going to give each tenant their own database anyway, the step to giving them their own app instance is mostly an infrastructure problem, not a code change.
Moving from multi-tenant to single-tenant
You rarely need to rewrite anything. The usual path:
- Make the tenant a deploy-time setting. Code that reads the tenant from each request keeps working; an instance that serves one customer simply always resolves to the same one.
- Move configuration out of code and into environment variables, so each instance can differ without a fork.
- Export one customer's data from the shared database into their own.
- Stand up their environment and test it on a temporary hostname.
- Cut over DNS, then keep the old rows read-only until you're sure.
Do this for the customers who need it, not everyone at once. Running a pooled tier and a siloed tier from the same codebase is normal.
Custom domains for each customer
Customers on their own instance usually want it on their own domain (app.customer.com rather than customer.yourapp.com). That needs three things per domain: a DNS record from the customer pointing at the instance, a way to prove they own the domain (usually a TXT record), and a TLS certificate issued and renewed automatically, typically through Let's Encrypt.
How Staxa does this
Staxa automates level 4. One API call creates a tenant, and each tenant is a Kubernetes namespace with its own application services, its own PostgreSQL or MySQL database, a subdomain with TLS, network policies and resource quotas. Environment variables are encrypted at rest.
curl -X POST https://api.staxa.dev/api/v1/tenants \
-H "Authorization: Bearer $STAXA_KEY" \
-d '{"name":"acme","db_engine":"postgres",
"services":[{"name":"web","source_type":"github",
"repo_url":"https://github.com/acme/app","port":3000}]}'
Call it from your signup or payment webhook, and each new customer gets their own environment without a ticket. The API streams progress while it builds and deploys, and the dashboard does the same thing for anyone who'd rather click. Restarts, redeploys, rollbacks, logs and custom domains are per tenant.
What it isn't: tenants share Staxa's cluster, separated by namespaces. If a contract requires hardware or a cloud account of the customer's own (level 5), that's a different product.
Frequently asked questions
Is single-tenant more secure than multi-tenant?
It removes the most common way SaaS products leak data: one customer's query returning another's rows. Each customer's data sits in its own database, behind its own application. It doesn't remove the need for security work elsewhere; a single-tenant instance with a weak password is still weak.
Is single-tenant more expensive?
Per customer, yes: every instance has a baseline of compute and storage even when idle. The larger cost is usually operational, in provisioning and upgrading many environments, which is why automation decides whether it's viable.
Can a SaaS be both single-tenant and multi-tenant?
Yes, and many are. A pooled tier serves self-serve customers cheaply, and a siloed tier serves customers who need isolation. The same codebase runs in both.
Is single-tenant the same as on-premise?
No. Single-tenant SaaS still runs on infrastructure you operate; the customer simply doesn't share it. On-premise means the software runs on infrastructure the customer operates.
Is a Kubernetes namespace enough isolation?
On its own, no: a namespace is a naming boundary. With network policies, resource quotas and role-based access control it becomes a strong boundary for most B2B SaaS. Workloads still share nodes and a kernel, so if you run code you don't trust, or a contract forbids shared hardware, you need separate nodes or clusters.
What is a tenant?
A tenant is one customer of your product (usually a company, not a single user), along with everything that belongs to them: their users, their data and their configuration.