EngineeringJul 27, 2026·12 min read

Supabase RLS Patterns for Multi-Tenant SaaS (2026 Guide)

Row-level security is the cheapest tenant isolation you will ever ship — if you avoid the four patterns that quietly leak data.

Muhammad Qitmeer
Muhammad Qitmeer
Co-Founder & CEO, Augere Labs
Share
Row-level security is the cheapest tenant isolation you will ever ship — if you avoid the four patterns that quietly leak data.

Multi-tenant isolation used to mean a separate database per customer. With Postgres row-level security you get isolation enforced by the database itself — but only if your policies are written the way the query planner and the auth layer expect.

The base pattern: org-scoped everything

Every tenant-owned table gets an org_id column. Membership lives in one table, and every policy resolves through it:

create table public.org_members (
  org_id uuid not null,
  user_id uuid not null references auth.users(id) on delete cascade,
  primary key (org_id, user_id)
);

create or replace function public.is_org_member(_org uuid)
returns boolean language sql stable security definer
set search_path = public as $$
  select exists (
    select 1 from public.org_members
    where org_id = _org and user_id = auth.uid()
  )
$$;

Then each table's policy is a single call: using (public.is_org_member(org_id)). One function, one place to audit.

Mistake 1: recursive policies

If a policy on org_members queries org_members, Postgres recurses and errors — or worse, you disable RLS to "fix" it. Always route membership checks through a security definer function, which bypasses RLS on the lookup.

Mistake 2: roles stored on the profile table

Never put is_admin on a row the user can update. Roles belong in a dedicated user_roles table that users can read but never write, checked by a has_role() security-definer function. A user-writable role column is a one-request privilege escalation.

Mistake 3: forgetting GRANTs

RLS is not the only gate. Postgres privileges come first, and Supabase no longer grants public-schema defaults. Every new table needs explicit grants alongside its policies, or the API returns permission errors that look like policy bugs.

Mistake 4: policies that kill the planner

Two rules keep RLS fast at scale:

  • Wrap auth calls so they evaluate once per query, not per row: (select auth.uid()) instead of auth.uid().
  • Index the column your policy filters on. An unindexed org_id turns every read into a sequential scan through other tenants' rows.

On a 4M-row table we have measured a policy rewrite from auth.uid() to (select auth.uid()) plus an org_id index cutting p95 read latency from 940ms to 22ms.

Read vs write policies

Write separate policies per command instead of one for all. Members read, admins write, service role does migrations. Separate policies make the intent auditable and stop an over-broad using clause from granting deletes you never meant to allow.

Testing isolation properly

Manual clicking does not prove isolation. Ship an automated test that signs in as tenant A, requests tenant B's row ids directly, and asserts empty results — for every table. Run it in CI. Cross-tenant leaks are almost always introduced by a new table that shipped without policies.

When RLS is not enough

Regulated workloads with contractual data-residency requirements still need schema- or database-per-tenant. For everything else, RLS with disciplined policies gives you isolation at a fraction of the operational cost.

FAQ

Frequently asked questions

Does RLS slow down Postgres?+

Only when policies are written badly. Wrapping auth calls in a subselect and indexing the tenant column keeps overhead in the low single-digit percent.

Where should user roles be stored?+

In a dedicated user_roles table checked by a security-definer function — never as a column on a profile row the user can update.

Is RLS enough for SOC 2?+

It satisfies the logical access control requirement, but you still need audit logging, access reviews, and encryption controls documented.

Schema-per-tenant or RLS?+

RLS for the vast majority of B2B SaaS. Schema or database per tenant only when contracts demand physical separation or data residency.

Building something similar?

Let's talk in 30 minutes.

Book an intro
© 2026 Augere Labs