Skip to content
Multi-tenant SaaS

Best Tools for Multi-Tenant SaaS Architecture

One codebase, many customers, data fully isolated — the stack that makes it work

Multi-tenancy is where most SaaS MVPs cut corners and pay for it later. I've built BookBed (booking SaaS, multiple property owners) and Callidus (clinic SaaS, multiple practices) with proper row-level data isolation from day one. These are the tools that make that practical.

The Tools
Supabase + Row-Level SecurityRecommended
Database / Auth

Postgres RLS policies enforce tenant isolation at the database level

RLS is the architectural decision that makes multi-tenancy safe. Instead of adding `WHERE tenant_id = ?` to every query and hoping nothing slips through, you write a policy once and the database enforces it everywhere. Supabase makes enabling and testing RLS policies straightforward. This is the foundation.

Used in production — BookBed, Callidus
StripeRecommended
Billing

Per-tenant subscription billing with customer + subscription objects

Model each tenant as a Stripe Customer with their own Subscription. Store the Stripe customer ID and subscription status in your tenants table. Use webhooks to keep subscription state in sync — never trust client-side Stripe data for access control. This pattern scales cleanly from 10 tenants to 10,000.

Used in production — BookBed, Callidus
Next.jsRecommended
Framework

Server components + middleware for per-tenant routing and auth

Next.js middleware is the right place to handle tenant identification — read the session JWT, extract the tenant ID, pass it down via headers or context. Server components can then query Supabase with the correct auth context. This keeps tenant logic out of every component.

ResendRecommended
Email

Per-tenant transactional email with sender domain customisation

Multi-tenant SaaS often needs to send email on behalf of tenants — booking confirmations from a property owner's brand, order receipts from a restaurant. Resend's domain management API lets you verify per-tenant sending domains programmatically. This is the detail most SaaS MVPs skip and regret.

Used in production — BookBed, Pizzeria Bestek
VercelRecommended
Hosting

Wildcard subdomain support for per-tenant custom domains

Vercel's wildcard domain handling (`*.yoursaas.com`) plus their middleware make per-tenant subdomains straightforward. For SaaS products that want each customer on `customer.yoursaas.com`, this is the lowest-friction deployment path. Custom apex domains require additional DNS verification logic — plan for it.

The Verdict

The minimal multi-tenant SaaS stack: Supabase (RLS for isolation) + Stripe (per-customer subscriptions) + Next.js (middleware for tenant context) + Resend (transactional email) + Vercel (subdomain routing). Get the RLS policies right in week one — retrofitting them onto an existing schema is painful.

In detail

How to choose your isolation model before you choose tools

The tool list above assumes one decision is already made: shared schema with a tenant_id column on every table. That is the right default for almost every SaaS starting out, but it is a decision, not a given. There are three models, and picking the wrong one is the mistake that actually hurts later:

  • Shared database, shared schema — one tenant_id column, RLS enforces the boundary. Cheapest to run, easiest to query across tenants for analytics, fastest to ship. This is what I used on both BookBed and Callidus.
  • Shared database, schema-per-tenant — a Postgres schema per customer. Sounds tidy; in practice migrations become N migrations and connection pooling gets awkward. Skip unless a contract forces it.
  • Database-per-tenant — full physical isolation. Only worth it when an enterprise buyer's compliance team puts it in the contract, or one tenant's load genuinely threatens the others.

The instinct to start with heavy isolation "to be safe" is backwards. Heavy isolation is harder to build, harder to migrate, and slower to ship. Start shared, and let a signed contract — not a hypothetical — be the thing that pushes you up a tier.

The criteria that actually matter (and the ones that don't)

When people evaluate a multi-tenant stack they over-index on benchmarks and brand. Here is what actually decides whether the build goes well:

Matters:

  • Can the isolation boundary be enforced in one place and tested? RLS wins here because the policy lives in the database and you can write a test that logs in as tenant A and asserts it cannot read tenant B. If your isolation is scattered across application WHERE clauses, you cannot prove it is correct.
  • Does billing state have a single source of truth? Subscription status must live server-side and be driven by webhooks. If access control ever reads a price or plan from the client, that is a security bug, not a billing detail.
  • How painful is a tenant-aware migration? Adding a column to a table that 10,000 tenants share is routine; reshaping data that is split per-schema or per-database is not. Favor the model that keeps migrations boring.

Doesn't matter (early):

  • Raw database benchmarks. At MVP and well beyond, you are nowhere near the limits of managed Postgres.
  • Multi-region replication. Real, but a year-two problem for the vast majority of products.
  • Microservices. A single well-structured app with clear tenant context beats a fleet of services with the boundary smeared across network calls.

Common mistakes I see repeatedly

Most multi-tenant failures are not exotic. They cluster around a few patterns:

  1. Trusting a tenant_id that came from the client. The tenant context must be derived from the verified session, never from a query parameter, request body, or header the browser can set. The whole isolation model collapses the moment a user can name their own tenant.
  2. Forgetting RLS on the table you added last week. A new table ships without a policy and is wide open by default in your queries until someone notices. Make "policy + isolation test" part of the definition of done for every new table, not a cleanup task.
  3. Building per-tenant custom domains before anyone asked. Wildcard subdomains are cheap; custom apex domains with per-tenant DNS verification and TLS are real work. Ship customer.yoursaas.com and add custom domains when a paying customer requests one.
  4. No plan for cross-tenant admin. You will need to view data across tenants for support and analytics. Decide early how a privileged role bypasses RLS safely (a dedicated service role with audited access) instead of bolting it on by weakening policies.
  5. Sending all email from one unverified domain. Deliverability quietly degrades, and tenants who want their own branding are stuck. Plan per-tenant sending domains up front, as the source list notes.

When a simpler, cheaper option is enough

Not every product needs the full five-tool stack on day one. Be honest about where you actually are:

  • One tenant per account, no shared resources, no cross-tenant features? That may be a single-tenant app wearing a multi-tenant costume. Don't pay the complexity tax for a boundary you never cross.
  • No subscriptions yet, just a few pilot customers? Skip Stripe entirely at first and manage access with a flag. Real recurring billing can wait until you have something worth billing for. Pizzeria Bestek (React + Supabase) shipped real customer-facing functionality without a subscription layer at all — payment infrastructure was scoped to what the product needed, not bolted on speculatively.
  • No need for tenant-branded email? A single verified sending domain is fine until a customer asks for their own. Add the per-tenant domain logic when that request lands.
  • No need for subdomains? Path-based tenancy (/t/customer) is simpler than wildcard DNS and good enough for plenty of B2B tools where the URL is never seen by end users.

The through-line: get data isolation right from week one, because retrofitting it is genuinely painful — and defer almost everything else until a real customer pulls it out of you. That discipline is a large part of why this kind of build ships without agency overhead: the work that goes in is the work the product actually needs.

Related

Want this stack built for you?