Supabase · Deployment guide

Supabase RLS deployment issues: permission errors and missing GRANTs

Short answer

RLS is not enough on its own. Supabase's Data API (PostgREST) needs explicit GRANT statements on every public-schema table for the anon / authenticated roles, or queries return permission errors even when your policies are correct.

Symptoms

  • Query returns “permission denied for table X” after enabling RLS.
  • Anon reads worked in dev, fail in production.
  • Inserts succeed but updates fail with policy violation.
  • Functions called from the client return “permission denied for function”.

Common causes

  • No GRANT statement for the table — Supabase no longer grants default privileges automatically.
  • Policy uses auth.uid() but user_id column is nullable or wrong type.
  • Function is SECURITY INVOKER but referenced as SECURITY DEFINER (or vice versa).
  • Realtime publication missing the table for subscribed clients.
  • Policy has FOR ALL but missing WITH CHECK for INSERT/UPDATE.

How DeployDoc checks this

  • Scans migrations for CREATE TABLE in public without matching GRANT statements.
  • Verifies every RLS-enabled table has policies covering every CRUD action in use.
  • Flags SECURITY DEFINER functions without explicit search_path set.
  • Detects realtime subscriptions for tables not in the supabase_realtime publication.

Fix it manually

  1. For every new public-schema table, add GRANT SELECT, INSERT, UPDATE, DELETE ON public.X TO authenticated;
  2. Add GRANT SELECT ON public.X TO anon; only if a policy allows anon reads.
  3. Always add GRANT ALL ON public.X TO service_role;
  4. Enable RLS, then add policies per action (SELECT / INSERT / UPDATE / DELETE).
  5. For SECURITY DEFINER functions, set search_path = public to prevent search-path attacks.

When to run a DeployDoc diagnosis

Right after enabling RLS on a new table, before pushing the migration to production.

Related guides