Database Architecture

PostgreSQL Schema Design for Multi-Tenant SaaS: Three Patterns Compared

Compare three PostgreSQL multi-tenant patterns: shared schema with RLS, schema-per-tenant, and database-per-tenant. Includes hash partitioning for shared schema at scale.

JusDB Team
May 9, 2025
5 min read
207 views

Choosing the right multi-tenant schema pattern in PostgreSQL has major implications for data isolation, query performance, and operational complexity. Here are the three main patterns.

Pattern 1: Shared Schema (Tenant ID Column)

sql
-- All tenants share tables; tenant_id column separates rows
CREATE TABLE orders (
  id BIGSERIAL PRIMARY KEY,
  tenant_id UUID NOT NULL REFERENCES tenants(id),
  amount NUMERIC(10,2),
  created_at TIMESTAMPTZ DEFAULT now()
);

-- Composite index for tenant-scoped queries
CREATE INDEX idx_orders_tenant_created ON orders (tenant_id, created_at DESC);

-- Combine with RLS for enforcement
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

Pros: Simple ops, shared indexes, easy to add tenants
Cons: Must ensure tenant isolation at app/RLS layer; noisy neighbor risk

Pattern 2: Schema per Tenant

sql
-- Each tenant gets their own schema
CREATE SCHEMA tenant_abc123;
CREATE TABLE tenant_abc123.orders (
  id BIGSERIAL PRIMARY KEY,
  amount NUMERIC(10,2),
  created_at TIMESTAMPTZ DEFAULT now()
);

-- Set search_path per connection
SET search_path = tenant_abc123;

Pros: Strong isolation, easy per-tenant backup/restore
Cons: Schema migrations must run per-tenant; hard to query across tenants; PostgreSQL limit ~10K schemas before performance degrades

Pattern 3: Database per Tenant

Each tenant gets a separate PostgreSQL database (or RDS instance).

Pros: Maximum isolation, independent scaling
Cons: High operational overhead; cross-tenant queries impossible; connection pooling complexity multiplied

Table Partitioning for Shared Schema at Scale

sql
-- Partition orders by tenant for large multi-tenant tables
CREATE TABLE orders (
  id BIGSERIAL,
  tenant_id UUID NOT NULL,
  amount NUMERIC(10,2),
  created_at TIMESTAMPTZ
) PARTITION BY HASH (tenant_id);

CREATE TABLE orders_p0 PARTITION OF orders FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE orders_p1 PARTITION OF orders FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE orders_p2 PARTITION OF orders FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE orders_p3 PARTITION OF orders FOR VALUES WITH (MODULUS 4, REMAINDER 3);

Key Takeaways

  • Shared schema + RLS is the most operationally scalable pattern for most SaaS applications
  • Schema-per-tenant provides stronger isolation but limits you to ~1,000 tenants before ops complexity explodes
  • Always index (tenant_id, primary_sort_column) for shared-schema queries
  • Hash partitioning on tenant_id distributes I/O for very large shared-schema deployments

JusDB Can Help

Choosing the wrong multi-tenant architecture is costly to change later. JusDB can help you design the right schema pattern for your SaaS workload from day one.

Share this article

JusDB Team

Official JusDB content team