Skip to main content

Multi-Tenancy in SaaS Boilerplates: 2026 Guide

·StarterPick Team
multi-tenancyb2bsaas-boilerplateorganizationsrbacbilling2026

Multi-Tenancy in SaaS Boilerplates: What to Look For 2026

Most SaaS boilerplate comparisons focus on auth and billing. But for B2B SaaS — any product where your customers are companies, not individuals — multi-tenancy is the most important feature to evaluate. Get it wrong at the start and you're rebuilding your data model six months later.

This is the buyer's checklist. Ten features to evaluate before you pick a boilerplate for a B2B SaaS product.

TL;DR

For B2B SaaS, evaluate boilerplates on: data isolation model (RLS vs schema vs DB-per-tenant), organization management (invite flows, member limits, org switching), RBAC (owner/admin/member at minimum), per-organization billing (org pays, not individual), and custom domain support. Most boilerplates cover the first three. Fewer handle per-org billing correctly. Almost none include custom domains out of the box.

Key Takeaways

  • Row-Level Security (RLS) is the right default for most SaaS — simple, scales to millions of orgs, Postgres and Supabase native
  • Schema-per-tenant for compliance-heavy industries (healthcare, legal, finance) requiring contractual data separation
  • DB-per-tenant only justified at $10K+/month per customer — Turso makes it cheap for the edge case
  • RBAC minimum viable: owner, admin, member — most boilerplates get this right
  • Per-org billing is often incomplete — verify the boilerplate handles org plan changes, not just individual plan changes
  • Top picks with complete multi-tenancy: Supastarter, MakerKit, Bedrock — all include org management + billing + RBAC

Feature 1: Data Isolation Model

The most fundamental multi-tenancy decision is how tenant data is separated. There are three patterns:

All tenants share one database, one schema. A organization_id column on every table identifies which tenant owns each row. Postgres Row-Level Security (RLS) policies enforce that queries can only access rows belonging to the current tenant.

-- Enable RLS on your table
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

-- Policy: users can only see their organization's data
CREATE POLICY "tenant_isolation" ON projects
  USING (organization_id = current_setting('app.current_tenant')::uuid);
// Set the tenant context on every database connection
async function withTenantContext(orgId: string, fn: () => Promise<void>) {
  await db.execute(sql`SET app.current_tenant = ${orgId}`);
  await fn();
  await db.execute(sql`RESET app.current_tenant`);
}

Boilerplates using RLS: Supastarter (Supabase), MakerKit (Supabase), Bedrock

When it's the right choice: pre-revenue through $1M ARR for most SaaS. Simple, cheap, scales to millions of tenants.

When it's not enough: compliance requirements where tenants need contractual data isolation (SOC 2 Type II with strict tenant separation clauses, HIPAA business associate agreements with database-level guarantees).

Schema-Per-Tenant

Each tenant gets their own Postgres schema (org_tenant_123.users, org_tenant_456.users). Same database, separate namespaces.

-- Create a schema for each new organization
CREATE SCHEMA IF NOT EXISTS "tenant_${orgId}";

-- Run migrations in the tenant schema
SET search_path TO tenant_${orgId};
CREATE TABLE users (id uuid PRIMARY KEY, ...);

Benefits: data isolation that holds up to compliance audits, easier per-tenant backups and restores.

Drawbacks: migrations run per-tenant (slow for 1,000+ tenants), cross-tenant analytics queries become complex, Prisma/Drizzle don't handle dynamic schema switching natively.

Best for: regulated industries with 10–500 enterprise clients.

Database-Per-Tenant

The most isolated option — each tenant has their own database. Historically expensive; Turso's per-tenant SQLite model makes it economically viable.

// Turso pattern: one database per organization
async function getOrgDb(orgSlug: string) {
  const client = createClient({
    url: `libsql://${orgSlug}-${TURSO_ORG}.turso.io`,
    authToken: TURSO_AUTH_TOKEN,
  });
  return drizzle(client);
}

Benefits: absolute isolation, easy tenant offboarding (drop one database), enterprise contracts that require physical data separation.

Drawbacks: Turso's 500-database free limit; no good tooling for running migrations across thousands of databases; SQLite limitations (no complex joins, concurrent write bottleneck).

What to look for in a boilerplate: Which isolation model is it built on? Can you migrate to a stricter model later? (Migrating from RLS to schema-per-tenant is painful — evaluate the ceiling before committing.)


Feature 2: Organization Management

The basics every B2B boilerplate should have:

✓ Organization CRUD (create, rename, delete)
✓ Member invite flow (email invite → accept link)
✓ Pending invite management (cancel, resend)
✓ Member removal
✓ Organization switcher (for users in multiple orgs)
✓ Per-org member limits tied to subscription plan

Common gaps to check:

Invite expiration: Does the invite link expire? After how long? Can admins resend? This gets complicated in UX — a user who "definitely got the invite" six days ago needs a resend path.

Multiple org membership: Can a user belong to multiple organizations? Most SaaS products need this — your customer's employee might use your tool through their employer and also have a personal account. Boilerplates that assume single-org membership need schema changes to support multiple memberships.

Organization switching UX: How does the user switch between orgs in the dashboard? Is it a page reload or instant? For React/Next.js apps, this usually means clearing the current org from state and re-fetching.

// Organization context in React
import { useOrganization } from "@/hooks/use-organization";

function OrgSwitcher() {
  const { currentOrg, userOrgs, setCurrentOrg } = useOrganization();

  return (
    <Select
      value={currentOrg.id}
      onValueChange={(orgId) => {
        setCurrentOrg(orgId);
        // Invalidate all org-scoped queries
        queryClient.invalidateQueries({ queryKey: ["org", orgId] });
      }}
    >
      {userOrgs.map((org) => (
        <SelectItem key={org.id} value={org.id}>
          {org.name}
        </SelectItem>
      ))}
    </Select>
  );
}

Feature 3: Role-Based Access Control (RBAC)

The minimum viable RBAC for B2B SaaS is three roles:

Owner
├── All permissions
├── Can delete the organization
├── Billing management
└── One per organization (usually)

Admin
├── All member permissions
├── Invite and remove members
├── Manage organization settings
└── Cannot delete organization or transfer ownership

Member
└── Access to product features (scoped by what the org pays for)

What to look for beyond the basics:

Custom roles: Some boilerplates (MakerKit) let you define custom roles beyond owner/admin/member. Necessary for products where different team members have different feature access (e.g., a "billing only" role for finance teams).

Permission-level granularity: Can you control permissions per feature, not just per role? "Members can view but not edit" is a common requirement.

RBAC enforcement at the API layer: It's not enough to hide UI elements based on role — the backend must enforce the same checks. Look for boilerplates where RBAC is enforced in middleware or API route handlers, not just in the component tree.

// RBAC middleware example (well-implemented)
export async function requirePermission(
  orgId: string,
  userId: string,
  permission: Permission
) {
  const member = await db.query.organizationMembers.findFirst({
    where: and(
      eq(organizationMembers.orgId, orgId),
      eq(organizationMembers.userId, userId)
    ),
  });

  if (!member || !hasPermission(member.role, permission)) {
    throw new Error("Forbidden");
  }

  return member;
}

Feature 4: Per-Organization Billing

This is where most boilerplates fall short. Most boilerplates wire billing to the user, not the organization. For B2B SaaS, the organization pays:

Correct B2B billing model:
Organization has a Stripe Customer ID
Organization has a subscription
Organization's plan determines what features members can access
Members can be added up to the plan's seat limit

Common boilerplate mistake:
User has a Stripe Customer ID
User has a subscription
Adding team members doesn't interact with billing

What to check specifically:

  1. Is the Stripe customer the org or the user? Check the schema — does the stripe_customer_id column live on the organizations table or the users table?

  2. Does plan gating check the org's plan? When you middleware-protect a feature, does the check read from organization.plan or user.plan?

  3. Per-seat pricing support: Can the boilerplate handle plans that charge per team member? (e.g., $20/seat/month). This requires integrating Stripe's quantity on subscriptions and updating it when members are added or removed.

// Per-seat billing: update quantity on member changes
async function addMemberToOrg(orgId: string, userId: string) {
  const org = await getOrg(orgId);
  const memberCount = await getMemberCount(orgId);

  // Update Stripe subscription quantity
  if (org.stripeSubscriptionId) {
    await stripe.subscriptions.update(org.stripeSubscriptionId, {
      items: [{
        id: org.stripeSubscriptionItemId,
        quantity: memberCount + 1,
      }],
    });
  }

  // Add member to database
  await db.insert(organizationMembers).values({ orgId, userId });
}

Feature 5: Custom Domains Per Organization

Almost no boilerplates include this, but many B2B SaaS products need it. "Your customers want to access your tool at app.theirdomain.com, not yourapp.com/org/their-name."

Custom domain support requires:

  1. DNS verification (prove the customer owns the domain)
  2. SSL certificate provisioning (Let's Encrypt or Cloudflare)
  3. Request routing (map incoming domain to the right org)
// Custom domain middleware
export function customDomainMiddleware(req: NextRequest) {
  const hostname = req.headers.get("host");

  // Check if this is a custom domain (not your main domain)
  if (!hostname?.endsWith(".yourapp.com")) {
    // Look up which org owns this domain
    const org = await db.query.organizations.findFirst({
      where: eq(organizations.customDomain, hostname),
    });

    if (org) {
      // Inject org context into the request
      const url = req.nextUrl.clone();
      url.searchParams.set("orgId", org.id);
      return NextResponse.rewrite(url);
    }
  }
}

Vercel handles the infrastructure part — you can add custom domains to Vercel deployments via the API. Railway and Render have similar APIs. What the boilerplate needs to provide is the database schema (org custom domain field) and the request routing middleware.


Feature 6–10: The Checklist

Beyond the big five, quickly evaluate:

6. Audit logs — Does the boilerplate log organization-scoped events? (member added, plan changed, data exported) Essential for enterprise customers who need SOC 2 compliance.

7. SSO/SAML support — Enterprise customers often require single sign-on via their IdP (Okta, Azure AD). This is rarely in boilerplates but worth knowing if your ICP includes enterprises.

8. Data export per tenant — Can each organization export their data? Required for GDPR compliance in EU markets.

9. Organization deletion / offboarding — What happens when an org cancels? Can you purge all their data cleanly? Most boilerplates handle this poorly — verify before you have your first churned enterprise customer.

10. API keys per organization — B2B SaaS often needs programmatic access. Does the boilerplate generate organization-scoped API keys (not user-scoped), with rate limiting per org?


Boilerplate Multi-Tenancy Scorecard

SupastarterMakerKitBedrockShipFast
Data isolationRLS (Supabase)RLS (Supabase)RLSNone
Org management✓ Full✓ Full
RBAC✓ (3 roles)✓ (custom roles)
Per-org billing
Per-seat billing
Custom domains
Audit logs
Multi-org membership

No boilerplate scores 10/10. Supastarter and MakerKit are the strongest multi-tenancy options in the Next.js ecosystem — both cover the critical path (isolation, org management, RBAC, per-org billing). Custom domains, audit logs, and SSO you'll add yourself.


The Right Question Before Buying

Before evaluating any boilerplate's multi-tenancy, answer this: Is your SaaS B2B or B2C?

If B2C (individual users, no team management), multi-tenancy is overhead. ShipFast or a simpler starter is the better choice.

If B2B (customers are companies, team collaboration is core), multi-tenancy is non-negotiable. Retrofitting it into a non-multi-tenant codebase takes 2–4 weeks. Starting with Supastarter or MakerKit costs $299 and takes 4 hours.

Browse multi-tenant boilerplates on StarterPick. Related: multi-tenancy patterns deep-dive and best B2B SaaS boilerplates compared.

Comments