Image
Build
Connect & operate
Design & teams
Start hereScope a build in one callBring a spec, a wireframe, or a paragraph. You leave with an architecture, a timeline, and a number.Book a scoping call
AI software
LLM & data systems
Vibe coding
Ready to ship?Put AI where the work isAgents, RAG, and private LLMs wired into the systems your team already uses — not a chatbot bolted to a homepage.Discuss an AI project
Domain firstWe learn your workflow before we model itRegulated, operational, or high-volume — the constraints belong in the schema, not in a training doc.Talk about your domain
Plan smarterEstimate before you commitCost ranges, scope templates, and the questions we ask in discovery — free, no form.Open the cost calculator
Real conversationsTalk with a technical leadNo SDR, no discovery gauntlet. The person on the call is the one who scopes the build.Book a call
Eric Lamanna
Author
Choosing a Multi-Tenant Data Model for Your SaaS on Postgres — featured image
9/22/2026

Choosing a Multi-Tenant Data Model for Your SaaS on Postgres

Most SaaS founders treat the tenant isolation question as a detail to sort out later. In reality, it is one of the first decisions that will outlive the codebase, because it shapes migrations, backups, per-customer pricing, compliance posture, and how much a noisy neighbor can hurt everyone else on the platform.

Postgres gives you three viable shapes for multi-tenant SaaS architecture: a single shared schema with a tenant_id column on every table, a separate schema per tenant inside one database, or a separate database per tenant. Each has honest trade-offs on cost, blast radius, and operational drag. The wrong pick will not sink you in month three, but it will show up as a painful rewrite in year two.

So which model actually fits the SaaS you are building?

The Three Shapes and What They Actually Cost

The shared-schema model puts every tenant's rows in the same tables, keyed by a tenant_id column, and relies on the application (or Postgres row-level security) to filter correctly. It has the lowest per-tenant cost and the simplest migrations: one ALTER TABLE touches every customer at once. The trade-off is blast radius. A missing WHERE clause can leak data across tenants, and a heavy query from one customer can starve the rest.

Schema-per-tenant keeps one database but gives each tenant its own namespace (tenant_42.invoices, tenant_42.users). Isolation is stronger, per-tenant backups and exports are trivial, and you can drop a customer's data with a single DROP SCHEMA. The cost shows up in the Postgres catalog: every new tenant multiplies pg_class, pg_attribute, and pg_index entries, and every migration has to be run N times. Practitioners commonly report the model breaking down past roughly 500 tenants due to that catalog and operational overhead.

Database-per-tenant gives the hardest isolation, per-tenant tuning, and the cleanest compliance story. It also multiplies your connection footprint and your ops surface. PostgreSQL's default max_connections is 100, and the docs recommend pooling once you exceed roughly 200 connections, so a thousand tenants with even a small pool each becomes a real infrastructure problem fast.

Three Tenant Isolation Models on Postgres
DimensionShared SchemaSchema per TenantDatabase per Tenant
Isolation strengthLogical (RLS)NamespacePhysical
Per-tenant costLowestLow-mediumHighest
Migration effortOne runN runs, batchedN runs, per-db
Blast radiusWideMediumNarrow
Per-tenant backupHardEasy (pg_dump)Native
Practical ceilingVery high~500 tenantsOps-bound
Best fitVolume SaaSMid-marketRegulated / enterprise
Illustrative: a visual comparison, not measured data.

Row-Level Security Is Not Optional in Shared Schema

If you pick shared schema, application-level filtering alone is a liability. It only takes one repository method or one ad-hoc analytics query to forget the tenant predicate. AWS makes this point directly: in a shared database, isolation often depends on developers correctly filtering by tenant in every SQL statement, which is a significant risk if a WHERE clause is forgotten.

Postgres row-level security, introduced in 9.5, lets you attach policies so SQL operations are filtered automatically by tenant. The pattern is straightforward:

  • Add tenant_id to every tenant-scoped table with a NOT NULL constraint.
  • Enable RLS on the table and write a USING policy that compares tenant_id to a session variable such as current_setting('app.tenant_id').
  • Set that variable at the start of every request or transaction, ideally in a middleware layer that no query can bypass.
  • Run application queries as a role that cannot BYPASSRLS. Reserve superuser and RLS-bypassing roles for migrations and backfills only.

RLS is not free. Every filtered query gains an extra predicate the planner has to satisfy, and complex policies with joins can defeat index-only scans. In practice the overhead is small if your indexes lead with tenant_id, which they should anyway.

Schema-per-Tenant Buys Isolation and Sells Migrations

Schema-per-tenant is the model most teams reach for when a single big customer asks for "their own database" and the team wants to avoid the operational weight of actually giving them one. It works well in the low hundreds of tenants and gives you per-schema pg_dump, per-schema search_path routing, and a very clean deletion story for GDPR requests.

The failure mode is migrations. Adding a column to fifty schemas is fine. Adding a column to five thousand schemas, transactionally, without locking any single tenant for too long, is a distributed systems problem you have to build tooling for. Most teams end up with a bespoke migration runner that iterates schemas in batches, tracks per-tenant migration state, and retries failures. A tenant migration strategy for this model needs to answer three questions up front: how you sequence schemas, how you handle partial failure, and how you keep the application compatible with both old and new shapes during the rollout.

Mechanical arms opening many labeled drawers of one filing cabinet at once, illustrating per-tenant migration fan-out.

The other quiet cost is connection multiplication. If your ORM opens pooled connections per schema (some do, especially when using SET search_path per checkout), you fan out fast. max_connections can only be changed at server start, and raising it increases shared memory allocation, so this is a capacity ceiling you plan for, not one you dial at runtime.

Database-per-Tenant Is a Compliance Answer, Not a Default

Database-per-tenant makes sense when a specific customer segment demands it: regulated healthcare, defense, financial infrastructure, or enterprise contracts that require data residency in a specific region. It gives you per-tenant encryption keys, per-tenant point-in-time recovery, and the option to physically move a noisy or sensitive tenant to its own hardware without touching anyone else.

The costs are real. Every database is a separate migration target, a separate backup schedule, a separate monitoring endpoint, and a separate connection pool. Cross-tenant analytics require ETL into a separate warehouse rather than a single GROUP BY tenant_id. Teams that pick this model without needing it usually regret it inside a year.

A common compromise is a hybrid: shared schema for the long tail of small tenants, dedicated database for a handful of enterprise accounts, with the application abstracting the routing behind a tenant directory. This is the same pattern used when clients ask about scaling considerations during an API development engagement, because the routing layer is where the isolation model actually lives.

When You Outgrow a Single Postgres

Vertical scaling on Postgres goes further than most teams expect. But past a certain point, one primary cannot hold every tenant, and you need horizontal partitioning. Citus is the pragmatic path here because it keeps the Postgres surface area intact. In Citus, tenant_id is the recommended distribution column for multi-tenant SaaS, because co-located shards keep a tenant's data on the same node and avoid cross-node traffic during joins, and Citus supports both row-based sharding by tenant_id and schema-based sharding so the choice you made earlier still applies.

The reference point most teams cite is Notion. Their Postgres architecture partitions data by workspace_id across 480 logical shards on 32 physical databases. The logical/physical split is the important idea: pick a shard count you will never realistically outgrow, then rebalance which physical database owns which logical shards as capacity demands change.

Relative Migration and Ops Effort as Tenants Grow
Relative Migration and Ops Effort as Tenants GrowShared schema: 1; Schema/tenant (100): 3; Schema/tenant (1k): 7; DB/tenant (100): 6; DB/tenant (1k): 10Migration effortOps surfaceShared schema11Schema/tenant (100)33Schema/tenant (1k)76DB/tenant (100)67DB/tenant (1k)1010
Effort scales roughly linearly with tenant count once you leave the shared-schema model. Illustrative: a visual comparison, not measured data.

A Decision Framework That Fits on One Page

Ignore the marketing copy from managed database vendors for a moment. The decision usually comes down to four inputs: expected tenant count at 24 months, the sensitivity of the data, whether any single customer will pay for isolation, and whether your team has the ops maturity to run N databases.

  • Under 1,000 tenants, standard SaaS data, small team: shared schema with RLS. Cheapest to run, easiest to migrate, and RLS closes the accidental-leak gap.
  • Under 500 tenants, mixed sensitivity, per-tenant exports common: schema-per-tenant. Invest early in a migration runner.
  • Regulated data or enterprise contracts requiring isolation: hybrid, with database-per-tenant reserved for the accounts that justify the ops cost.
  • Growth trajectory beyond a single primary: shared schema keyed by tenant_id from day one, with Citus or an equivalent sharding layer as the exit plan.

The failure mode across all four is the same: teams pick the model that flatters their ambition rather than the one that fits their operational reality. A two-person team running fifty databases will spend more time on backups than on features. A ten-person team running a single shared schema for a Fortune 500 customer will lose the deal.

Where Each Model Typically Fits a SaaS Portfolio
Where Each Model Typically Fits a SaaS PortfolioShared schema + RLS: 65%; Schema per tenant: 20%; Database per tenant: 10%; Hybrid routing: 5%Shared schema + RLS65%Schema per tenant20%Database per tenant10%Hybrid routing5.0%
A rough split of models teams end up on, weighted by tenant count rather than customer count. Illustrative: a visual comparison, not measured data.

Where This Decision Meets the Rest of the Build

The tenant model touches almost every other architectural choice: how you structure background jobs, how you scope caches, how you route webhooks, how you build admin tooling, and how you eventually plug in retrieval-augmented AI features where per-tenant embeddings need the same isolation guarantees as the underlying rows. Teams evaluating custom LLM development on top of a SaaS product usually discover their tenant model constrains what the AI layer can and cannot do, months after the model was chosen.

Get the isolation shape right early, write the routing layer as a first-class abstraction, and treat the migration runner as production code rather than a script. The teams that do this ship features faster in year two, because they are not paying interest on a decision made in week three. For a broader view of tenant-safe engineering practices, our notes on scalable application patterns and large-scale TypeScript apps cover the application-side habits that make any of these three models survivable. If you would rather have a senior team scope this with you, that is what our engineering group does.

Author
Eric Lamanna
Eric Lamanna is a Digital Sales Manager with a strong passion for software and website development, AI, automation, and cybersecurity. With a background in multimedia design and years of hands-on experience in tech-driven sales, Eric thrives at the intersection of innovation and strategy—helping businesses grow through smart, scalable solutions. He specializes in streamlining workflows, improving digital security, and guiding clients through the fast-changing landscape of technology. Known for building strong, lasting relationships, Eric is committed to delivering results that make a meaningful difference. He holds a degree in multimedia design from Olympic College and lives in Denver, Colorado, with his wife and children.