Skip to content
Guide · Engineering

Yash Suthar, founder of StackWiseUpdated 6 min read

Short answer

Multi-tenant SaaS architecture means one application serves many customer organizations, called tenants, while keeping each tenant's data separate. The three common models are a shared database with a tenant_id column and row-level security, a schema per tenant, and a database per tenant. Most new SaaS products start with the shared model and move large or regulated tenants to dedicated databases later.

Key takeaways

  • A shared database with tenant_id and PostgreSQL row-level security is the sensible default for most new SaaS products.
  • Schema-per-tenant and database-per-tenant buy stronger isolation at the cost of harder migrations and higher running costs.
  • Isolation must hold everywhere: queries, caches, file storage, background jobs and search indexes.
  • Plan per-tenant billing and rate limits from the start; both are painful to retrofit.

What a tenant is

A tenant is the customer account that owns data in your product, usually a company or team rather than one person. When a company signs up for your project-management app and invites twenty colleagues, the company is the tenant and the twenty people are users inside it.

In a multi-tenant system, every tenant runs on the same code and infrastructure. The alternative, single-tenancy, gives each customer a separate deployment: simple to reason about, but expensive to run and slow to update. That is why most SaaS products are multi-tenant, at least at the application layer.

The three tenancy models compared

  • Shared database, shared schema. Every tenant-owned table has a tenant_id column, every query filters by it, and PostgreSQL row-level security (RLS) enforces the filter inside the database.
  • Schema per tenant. One database with a separate schema per tenant, each holding the same tables. The app sets search_path for each request.
  • Database per tenant. Each tenant gets its own database, sometimes its own server or region. The app looks up the tenant's connection details per request.
Multi-tenant database models in PostgreSQL
FactorShared DB + tenant_id + RLSSchema per tenantDatabase per tenant
IsolationLogical, enforced by RLS policiesSeparate tables; relies on search_path and grantsStrongest, separate database and credentials
Cost per tenantLowestLow to mediumHighest
MigrationsRun once for everyoneRun once per schemaRun once per database, needs orchestration
Cross-tenant reportingPlain SQLQueries across schemas or a warehouseNeeds a warehouse or ETL
Noisy-neighbor riskHighestHigh, same serverLower; lowest on separate servers
Scale of tenant countVery largeGets hard to manage as schemas multiplyBest with fewer, larger tenants
Best forMost B2B and B2C SaaSModerate tenant counts with per-tenant customizationEnterprise, regulated or data-residency tenants

Tenant isolation with PostgreSQL row-level security

In the shared model, one missing tenant filter can leak data between customers. Row-level security moves that check into the database, so a bug in one query cannot return another tenant's rows.

  1. 1Add tenant_id to every tenant-owned table and make it the leading column of the indexes those queries use.
  2. 2Enable RLS on each table and create a policy such as USING (tenant_id = current_setting('app.tenant_id')::uuid).
  3. 3Set the tenant at the start of each transaction with SET LOCAL or set_config(..., true), so the value cannot leak to the next request on a pooled connection.
  4. 4Connect the app as a role that does not own the tables. Superusers and roles with BYPASSRLS always skip policies, and table owners skip them unless you also run ALTER TABLE ... FORCE ROW LEVEL SECURITY.
  5. 5Write tests that act as tenant A and try to read, update and delete tenant B's records.

Isolation is wider than the database. Prefix cache keys and file-storage paths with the tenant ID, carry it in every background job, and filter search and vector indexes by tenant. An AI search feature that queries documents without a tenant filter will leak data.

Authentication, roles and billing per tenant

Users sign in as people; authorization happens per tenant. A memberships table (user_id, tenant_id, role) lets one person belong to several organizations with a different role in each, such as owner, admin, member or viewer. Resolve the active tenant from the subdomain, URL or a tenant switcher, then check membership on every request. Never trust a tenant ID from the client on its own.

Enterprise customers often ask for single sign-on (SAML or OIDC), configured per tenant. Leave room for it even if you build it later.

Billing attaches to the tenant, not the user. Store the payment provider's customer and subscription IDs on the tenant record, map plans to limits such as seats, projects or API calls, and update them from webhooks rather than the checkout redirect. The Stripe vs Razorpay guide covers how that works with each provider.

Noisy neighbors and rate limiting

On shared infrastructure, one tenant running a huge import or hammering your API can slow everyone else down. The defenses come in layers.

  • Per-tenant rate limits on the API, keyed by tenant and plan, returning 429 responses with a Retry-After header.
  • Queues for heavy work such as imports, exports and AI jobs, with per-tenant concurrency caps so one tenant cannot occupy every worker.
  • Query guardrails: statement_timeout, enforced pagination and indexes that start with tenant_id.
  • Per-tenant monitoring, so you can see who uses what and move a heavy tenant to dedicated resources.

StackWise built LimitYourAPI, a developer SaaS for API rate limiting on Go and Redis Lua scripts, so per-tenant limits are a problem it has worked on directly.

Migrations across tenants

Schema changes are where the models differ most day to day. In a shared database you run one migration. With a schema or database per tenant you run the same migration hundreds of times, and some will fail partway, so you need tooling that records each tenant's version and can retry.

  • Use expand-and-contract changes: add the new column, backfill, switch the code, then drop the old column in a later release.
  • Build indexes on large tables with CREATE INDEX CONCURRENTLY so writes are not blocked. It cannot run inside a transaction block.
  • Backfill in batches instead of one huge UPDATE.
  • Run migrations in CI against realistic data before they reach production.

Which model should you choose?

Start with a shared database, tenant_id on every tenant table and row-level security, unless you already know you need something else. It is the cheapest to run, the simplest to migrate and, tested properly, strong enough isolation for most B2B products.

Give some tenants their own database when contracts demand it: data in a specific region, separate encryption keys or separate backups. A hybrid is common, with most tenants in the shared database and a few enterprise tenants on dedicated databases behind the same app. Schema-per-tenant suits a moderate number of tenants that each need custom tables.

StackWise designs tenancy as part of its SaaS development work, starting with the shared model and a documented path to dedicated tenants.

Want a number for your project?

Answer a few questions and get a starting price straight away, then a written quote.

Questions people also ask

Something else? Ask in the chat and get an answer straight away.

  • In single-tenant SaaS, each customer gets a separate copy of the application and database. In multi-tenant SaaS, all customers share one application, and their data is separated by tenant ID, schema or database. Multi-tenant is cheaper to run and easier to update; single-tenant gives maximum isolation and is usually reserved for enterprise or regulated customers.

Free consultation

Have a project in mind?

Tell us what you're building. We'll suggest the simplest way to ship it, and tell you honestly if we're not the right fit.

Email us
  1. 1

    Tell us about the project

    A few quick questions in the chat, or an email.

  2. 2

    Reply within 24–48 hours

    With questions, a rough scope and a starting price.

  3. 3

    Free call, then a written quote

    No commitment until you're happy with it.