Multi-Tenant SaaS Architecture: Patterns We Actually Use

Shared database, schema-per-tenant, or database-per-tenant? We break down the trade-offs and share our go-to patterns for building SaaS with Next.js and PostgreSQL.

Majid Hussain· Founder & CEO, DIGIT9 min read

Multi-tenancy is a foundational decision in SaaS architecture. Get it wrong and you pay for it on every feature you ship. Here is how DIGIT approaches it after building 12+ SaaS products.

The Three Models

Shared database, shared schema: All tenants share tables, with a tenant_id column on every row. Lowest infrastructure cost, highest operational complexity. Row-level security (RLS) in PostgreSQL makes this manageable — we enable RLS on all tenant-scoped tables and enforce it at the database level, not the application level.

Shared database, schema-per-tenant: Each tenant gets their own PostgreSQL schema (namespace). Migrations must run across all schemas. We use this when tenants need significantly different data models or strong data isolation guarantees without the cost of separate databases.

Database-per-tenant: The strongest isolation, but operationally expensive. We only recommend this when contractual data residency requirements or enterprise compliance mandates it.

Our Default: RLS on Shared Schema

For 80% of SaaS products, shared database with Row-Level Security is the right choice. The setup cost is a few hours; the operational savings over the product lifetime are enormous.

The critical step is making RLS invisible to the application layer. We use a PostgreSQL function that sets app.current_tenant_id as a session variable at connection time. All RLS policies reference this variable. Application code never writes WHERE tenant_id = ? — the database enforces it automatically.

Connection Pooling

PgBouncer in transaction mode is mandatory for SaaS workloads. Without it, each tenant request holds a PostgreSQL connection for the duration of the request. With 1,000 tenants and 100 concurrent requests, you exhaust PostgreSQL's connection limit instantly.

We run PgBouncer as a sidecar container in our Kubernetes deployments, configured with a pool size of 20 connections per application instance.

Tenant Onboarding

Automate it completely. New tenant creation should take under 100ms and require zero manual intervention. Our onboarding flow: create tenant record → generate slug → set up default configuration → seed initial data → create admin user → send welcome email. All transactional, all in a single database transaction.

Related Articles

Built by DIGIT

Need help building something like this?

DIGIT has shipped 1,000+ projects across web, mobile, AI and cloud. Let's talk about yours.