Skip to main content
WZ SolutionsWZ SolutionsSoftware Engineering
Software Engineering

Postgres patterns for multi-tenant enterprise platforms

Row-level security, partitioning and connection strategy — the three decisions that determine whether your multi-tenant platform survives its tenth customer.

WZ Engineering1 min read

Every product platform we build eventually serves multiple organisations from one deployment. The isolation model chosen in week two determines what is possible in year two, so it deserves more thought than it usually gets.

Shared schema with row-level security

For most enterprise platforms, a shared schema with Postgres row-level security is the right default. One migration path, one connection pool, and isolation enforced by the database rather than by every query an application developer writes.

alter table invoices enable row level security;

create policy tenant_isolation on invoices
  using (tenant_id = current_setting('app.tenant_id')::uuid);

The critical detail is that the setting must be applied per transaction by a trusted layer, and the application role must not be able to bypass RLS. A single connection that forgets to set the tenant, running as a superuser or table owner, defeats the whole model.

Partition the tables that grow without limit

Meter readings, audit logs and transaction histories grow monotonically. Range partitioning by month keeps indexes small, makes retention a partition drop rather than a mass delete, and lets the planner skip most of the table for time-bounded queries.

Pool connections deliberately

Postgres connections are expensive and serverless runtimes multiply them. Use a transaction-mode pooler in front of the database, keep transactions short, and be aware that transaction-mode pooling rules out session-level features such as prepared statements held across statements or advisory locks scoped to a session.

  • Index every foreign key you filter or join on — Postgres does not add these for you
  • Prefer composite indexes led by tenant_id on multi-tenant tables
  • Use covering indexes to keep hot read paths index-only
  • Measure with EXPLAIN (ANALYZE, BUFFERS) against production-scale data, not a seed set
Keep reading

Related articles

Working on something like this?

If any of the above is a problem you are living with right now, we would be glad to compare notes.

Or email walter.mussagy@gmail.com — we reply within one business day.