Guide · 7 chapters
Multi-tenant SaaS, end to end
Postgres RLS, RBAC, metered billing, and a SOC 2-ready audit trail — wired in before the first tenant signs.
Chapter 01
Why multi-tenant
Multi-tenant scales sublinearly with customer count — one stack, many tenants. Single-tenant scales with each customer. The trade-offs are economic, regulatory, and operational, and they should be picked deliberately, not by accident. This guide walks the multi-tenant pattern we ship every time.
Chapter 02
Tenant isolation in Postgres RLS
We enforce tenant isolation at the database, not the application. Every tenanted table has a tenant_id column NOT NULL. Every connection sets a current_tenant() session variable on connect. Every table has an RLS policy that filters by current_tenant() = tenant_id.
- Connection middleware sets current_tenant() on every connection.
- Policies are CREATE POLICY, not table grants — auditors see them in pg_policies.
- We test the RLS policies with a deny-by-default test for every table.
- Admin queries use a separate database role that bypasses RLS — gated by IAM.
Chapter 03
RBAC — roles inside the tenant
Inside a tenant, you need a roles model. We default to three roles (owner, member, billing) with the ability to add custom roles. Permissions are checked in the application layer for UX, then re-checked at the database via RLS for safety.
Chapter 04
Metered billing with Stripe
Usage-based billing is wired into the business logic, not patched in later with a webhook. We meter the actions that matter (API calls, seats, storage) at the source — emit events to Stripe in batches, reconcile nightly. Billing failures alert engineering, not just finance.
Chapter 05
Audit logs the way SOC 2 expects them
Append-only log of every action, with actor, target, and changeset. Stored separately from the operational database so a tenant compromise doesn't taint the audit trail. Retained for the period the contract requires.
Chapter 06
Provisioning a new tenant
Self-serve where possible, sales-assisted for enterprise. The provisioning flow creates the tenant record, seeds default config, sends the welcome email, and emits an analytics event — all in one transaction.
Chapter 07
Handling tenant data export and deletion
GDPR and SOC 2 both require this. We model tenant export as a long-running job, with progress visible to the customer. Deletion is soft-delete with a 30-day window, then hard-delete with audit. Export and deletion both produce a signed PDF receipt.
More guides
Building production AI agents
Tool use against real APIs, eval harnesses, observability, and the kill-switches you'll want.
Core Web Vitals — the playbook we run
LCP, INP, CLS — how we diagnose, fix, and lock in. With the GitHub Action we use to enforce.
WordPress to Next.js without losing SEO
Redirect maps, content migration, hreflang, and the post-launch monitoring that catches misses.
Run this with your team
Book the workshop version.
A half-day workshop with your team — same content, your codebase. We tailor the chapters to where your team is today.