Web Architecture

Multi-Tenant SaaS Architecture: The Supabase + RLS Approach and Its Limits

How to implement multi-tenant isolation using Supabase and Row-Level Security, why it works well below $5M ARR, and the specific signals that indicate you've outgrown it.

April 9, 202610 min read
multi-tenant architectureSaaSSupabasePostgreSQLNext.js

Multi-tenancy is one of the few architectural decisions in SaaS where the wrong choice doesn't just create technical debt — it creates a security incident. Cross-tenant data leakage at the database layer is a category of mistake that ends companies. The architecture you choose determines whether isolation is enforced at the database level or depends on every developer remembering to include the right WHERE clause on every query.

Most content on this topic covers the conceptual model (shared database vs. schema-per-tenant vs. database-per-tenant) without getting into implementation specifics. This post covers the specific implementation that works for most SaaS products below $5M ARR: Supabase with PostgreSQL Row-Level Security. What it does well, what its exact limits are, and the signals that tell you it's time to move to a different isolation model.

The Three Models and Their Real Tradeoffs

Shared database, row-level isolation. All tenants share tables separated by tenant_id. Simple schema, one migration path regardless of tenant count. The risk: a missing WHERE tenant_id = $1 exposes cross-tenant data. Row-Level Security at the database level mitigates this, which is why it's the right default combination.

Schema-per-tenant. Each tenant gets their own PostgreSQL schema within a shared database. Better logical isolation, but migration complexity scales with tenant count: 5 seconds on one schema becomes 90 minutes across 1,000. Schema lifecycle operations add overhead most early-stage teams don't have capacity for.

Database-per-tenant. Maximum isolation, highest operational complexity. Each tenant requires a managed database instance. Workable at 20 enterprise tenants; untenable at 2,000 SMB tenants. The right model for high-compliance enterprise contracts, not a general default.

The Supabase + RLS Implementation

For SaaS products below $5M ARR, Supabase with PostgreSQL Row-Level Security gives you shared-schema simplicity with isolation enforced at the database level rather than in application code.

The pattern used in the Meridian Analytics project:

Every tenant-scoped table gets a tenant_id UUID NOT NULL column, indexed, with a foreign key to the tenants table. No exceptions. The RLS policy reads the current tenant from a session variable set by the application before any query runs.

-- Enable RLS on every tenant-scoped table
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

-- Policy: users can only see rows matching their tenant
CREATE POLICY tenant_isolation ON projects
  USING (tenant_id = current_setting('app.current_tenant_id')::uuid);

The application sets the tenant context on every database session before executing queries:

SET LOCAL app.current_tenant_id = '${tenantId}';

In a Next.js App Router application, this happens in a middleware layer that reads the tenant from the authenticated session before any database operation runs. Even if a developer omits WHERE tenant_id = ?, the RLS policy enforces it unconditionally. The application cannot return cross-tenant data — isolation doesn't depend on code review.

What the Supabase + RLS Approach Does Well

Single migration path. Schema changes run once, apply to all tenants, and take the same time regardless of tenant count. At 10 tenants or 10,000 tenants, ALTER TABLE runs the same migration.

Low operational overhead. Supabase handles connection pooling via PgBouncer, manages the PostgreSQL instance, and provides a dashboard for monitoring. This eliminates a substantial amount of infrastructure work at the early stage.

Integrated auth. Supabase Auth provides JWT-based authentication with tenant context embedded in token claims. RLS policies can reference auth.uid() and auth.jwt() directly, often eliminating the need for separate session context management.

Scalability within the shared model. A single Supabase project handles millions of rows across thousands of tenants cleanly. The bottleneck is connection count — the Pro plan provides 200 direct connections with PgBouncer multiplexing beyond that — not RLS overhead.

The Specific Limits

RLS overhead on complex policies. On simple policies (tenant_id = current_setting(...)), overhead is negligible — under 1ms per query. On policies with subqueries or joins to other tables, overhead can reach 5–15ms per query. At 500 queries per second, that's measurable. Audit RLS policies if you see unexplained P95 latency increases.

Connection pool constraints at scale. Supabase Pro provides 200 direct connections, with PgBouncer multiplexing in transaction mode. The SET LOCAL command used for session-level tenant context doesn't work reliably in transaction mode pooling — the pattern above wraps it in an explicit transaction. Above roughly 1,000 concurrent active users, connection lifecycle management becomes non-trivial.

Cross-tenant analytics require explicit design. Admin-level reporting across all tenants needs either a service role connection that bypasses RLS (security risk if misused) or a separate analytics pipeline outside the RLS model. This is solvable, but it's a common oversight in early implementations.

Tenant-specific customization has a ceiling. Data residency requirements, custom retention policies, or BYOK encryption require per-tenant database configuration that the shared model can't support. These are enterprise features at $50k+ ACV — not a concern at the early stage, but it's the ceiling.

When to Move to a Different Model

Signals that you've outgrown the shared Supabase + RLS model:

At the transition point, schema-per-tenant is usually the right next step — single PostgreSQL instance, single migration surface, schema-level isolation. Database-per-tenant is for the 10–50 enterprise tenant scenario at $20M+ ARR with hard compliance requirements.

Implementation Checklist

For a new SaaS product implementing multi-tenancy on Supabase:

Wiring this into Next.js App Router's server client is covered in Next.js App Router patterns for SaaS. For connection pooling strategy and schema migration discipline, see database decisions at SaaS scale. If you're evaluating whether this model fits your current requirements, the architecture consulting engagement maps your tenant count and compliance requirements to the right isolation strategy.

Apply

If this maps to a problem you're working on.

I work with $1M–$20M ARR founders whose digital investment isn't producing the return it should. Applications reviewed personally within 48 hours.

2 Diagnostic slots / month · 2–3 full engagements / quarter · 48h review