Skip to content

Engineering · 11 min read

Multi-Tenant SaaS Architecture: Shared Schema, Row Level Security or a Database per Tenant?

How to choose a multi-tenant SaaS architecture: shared tables with row level security, schema per tenant or database per tenant, and the trade-offs.

Zain Khalid MalikZain Khalid MalikCTO & Co-founder, Innovation InsightPublished
SaaS dashboard with per-account usage charts

Short answer

Most B2B SaaS products should start with shared tables, a tenant ID on every row and row level security in PostgreSQL, because it is the cheapest model to run and the fastest to onboard customers. Choose a database per tenant when contracts, regulation or very large accounts demand physical separation. Schema per tenant sits in between and suits tens of tenants, not thousands. Decide before the first table exists; changing later is a rewrite.

What multi-tenant architecture means

A multi-tenant SaaS product serves many customer accounts, called tenants, from one application. The architecture question is how you keep each tenant's data separate while sharing as much infrastructure as possible. There are three standard answers, and the choice affects cost, onboarding speed, compliance and how hard the product is to operate. This guide explains each model, where it breaks, and how we chose between them on four products that are in production today.

The three tenancy models compared

Shared tablesSchema per tenantDatabase per tenant
How isolation worksTenant ID column on every row, enforced by row level security or a mandatory filterEach tenant has its own schema inside one databaseEach tenant has its own database, often its own storage bucket
Running costLowestLow to mediumHighest; grows with every tenant
Onboarding a tenantInsert a rowCreate a schema and run migrationsProvision a database, run migrations, register the connection
MigrationsRun onceRun once per schemaRun once per database, needs orchestration
Noisy neighbour riskHighestMediumLowest
Per-tenant backup and restoreHardPossibleSimple
Data residency per customerNot possible in one databaseNot possible in one databaseYes, place the database in the region
Comfortable scaleThousands of tenants and moreTens to low hundredsTens to hundreds with automation

Model 1: shared tables with row level security

Every table that holds customer data gets a tenant ID column. In PostgreSQL you then enable row level security on the table and write a policy that only returns rows whose tenant ID matches a value set for the current session or carried in the user's token. From that point the database itself refuses to return another tenant's rows, even if a developer forgets a WHERE clause. Supabase builds its whole authorisation model on this feature, which is one reason row level security now appears in so many job briefs.

  • Strength: one schema, one migration, one connection pool. A new customer is a new row, so self-service sign-up is instant.
  • Strength: reporting across all tenants for your own analytics is a single query.
  • Weakness: a table without a policy, or a role that bypasses row level security, exposes everything. Table owners and superusers skip policies by default, so the application must connect as a role that does not.
  • Weakness: one tenant's heavy query competes with everyone else's. You need indexes that lead with the tenant ID, query timeouts and rate limits.

If your database or ORM does not support row level security, the same model works with scoping enforced in one data-access layer. It is weaker, because the rule lives in code that can be bypassed, so back it with automated tests that try to read across tenants. We used organisation-scoped rows for TamTracker on PostgreSQL and business-scoped documents for Solas on MongoDB.

Model 2: schema per tenant

One database, with a separate schema (a namespace containing its own copy of every table) for each tenant. The application switches schema per request. Isolation is clearer than a tenant ID column, and you can back up or customise one tenant. The cost appears in operations: every migration runs once per schema, so a release that takes seconds with ten tenants takes many minutes with a thousand, and a migration that fails halfway leaves tenants on different versions. Schema per tenant suits products with a modest number of larger customers who need per-tenant customisation. It is rarely the right choice for self-service SaaS.

Model 3: database per tenant

Each tenant gets a dedicated database, and usually dedicated file storage. This is the strongest isolation short of separate deployments. A customer can be restored, exported, moved to another region or deleted without touching anyone else, and a security questionnaire that asks whether data is stored separately gets a plain yes.

The price is automation. You need a catalogue that maps tenants to connections, a provisioning job that creates and migrates a database at sign-up, a migration runner that upgrades every tenant and reports failures, and connection management that does not open thousands of idle pools. On Edge OCR we gave each organisation its own MongoDB database with dynamic model loading, per-tenant connection pooling and a circuit breaker on connections. On GovDoc AI every account gets a dedicated database and S3 bucket, provisioned automatically, because the product handles government contract documents.

A hybrid is common in practice: shared tables for self-service plans and a dedicated database for enterprise accounts that pay for it. It works, but you now test two code paths on every release, so adopt it only when a real contract requires it.

How to choose

If this is trueChoose
Many small or mid-size customers, self-service sign-up, price-sensitive plansShared tables with row level security
Contracts or regulators require separate storage, per-customer keys or regional residencyDatabase per tenant
A few dozen larger customers who each need custom fields or their own backupsSchema per tenant, or database per tenant if growth is likely
Mostly small customers plus a few enterprises with strict security reviewsShared tables now, designed so one tenant can be moved to its own database
You do not know yetShared tables, with tenant scoping in one layer and cross-tenant tests from the first sprint

Isolation is more than the database

Teams that get the tables right still leak data through the parts around them. Whichever model you choose, check each of these.

  • File storage: per-tenant prefixes or buckets, and signed URLs that expire. A guessable public URL is a data leak.
  • Caches: the tenant ID belongs in every cache key.
  • Search indexes and vector stores: filter by tenant on every query, or use an index per tenant. This matters for AI features, where retrieval can pull another customer's document into an answer.
  • Background jobs: carry the tenant ID in the job payload and set the tenant context before any query runs.
  • Logs and analytics: tag events with the tenant, and keep customer data out of shared log lines.
  • Admin tools: staff impersonation needs its own permission and an audit record the customer can ask for.

Roles and permissions inside a tenant

Tenant isolation answers which account a user belongs to. Role-based access control (RBAC) answers what that user may do inside it. Keep the two separate: a membership table links users to organisations with a role, and permissions are checked on the server for every request. Users who belong to several organisations are normal in B2B products, so model membership as many-to-many from the start. On Edge OCR permissions are enforced with decorators at resolver level alongside subscription guards, so a role check and a plan check happen in the same place.

Billing follows the tenant

Subscriptions, seats and usage limits attach to the organisation, not the user. Store the plan and its limits in your own database and update them from payment-provider webhooks, so the application never has to call the provider to decide whether a feature is allowed. If your tenants bill their own customers, as agencies do on TamTracker, you need a platform model such as Stripe Connect. Our API development and integration page covers how we make those webhooks safe to retry.

Testing tenant isolation

  1. Seed two tenants with similar data in every test environment.
  2. For every endpoint, add a test that signs in as tenant A and requests a record that belongs to tenant B. It must fail.
  3. Run the same checks against background jobs, exports and search.
  4. Run the suite in CI on every pull request, and treat a failure as a release blocker.
  5. Before launch, run a load test in which one tenant sends heavy traffic, and watch the other tenant's response times.

Moving between models later

Going from shared tables to a database per tenant is possible, but it is a project: export one tenant's rows, load them into a new database, switch the connection and verify nothing was missed. It is far easier if every table already has a tenant ID, no query joins across tenants and all data access goes through one layer. Going the other way, from many databases to shared tables, is rarer and harder, because primary keys collide. This asymmetry is why we advise starting with shared tables unless you already know you need physical separation.

We design the tenancy model during a $3k to $8k discovery sprint and record the decision before any feature work. See multi-tenant SaaS development for how we build these foundations, and the SaaS development cost guide for budgets.

Sources

Need a number for your project?

Send a short brief and get a written estimate.

A senior engineer replies within one business day. No sales call required.

Zain Khalid Malik, CTO & Co-founder, Innovation Insight

Zain Khalid Malik

CTO & Co-founder, Innovation Insight

Zain owns architecture, engineering standards and the platform team at Innovation Insight. He sets the bar for code quality, security and the tooling every squad ships with.

LinkedIn
FAQ

Related questions.

What is the best multi-tenant architecture for SaaS?

For most B2B SaaS, shared tables with a tenant ID and row level security. It costs the least to run and onboards customers instantly. Use a database per tenant when contracts or regulation require physical separation.

Is row level security safe enough for production?

Yes, when every tenant table has a policy, the application connects with a role that cannot bypass policies, and automated tests try to read across tenants. It should be combined with tenant-aware storage, caches and jobs.

When should each tenant have its own database?

When customers need separate backups, their own encryption keys, a specific region for their data, or a contract that states data is stored separately. Enterprise and regulated customers often ask for at least one of these.

How many tenants can schema per tenant handle?

It works comfortably for tens to low hundreds. Beyond that, running every migration once per schema slows releases and makes failures harder to recover from.

Can we change the tenancy model after launch?

Yes, but it is a significant migration. Moving from shared tables to dedicated databases is manageable if every table has a tenant ID and data access goes through one layer. Plan for it early even if you never do it.