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
- For every new public-schema table, add
GRANT SELECT, INSERT, UPDATE, DELETE ON public.X TO authenticated; - Add
GRANT SELECT ON public.X TO anon;only if a policy allows anon reads. - Always add
GRANT ALL ON public.X TO service_role; - Enable RLS, then add policies per action (SELECT / INSERT / UPDATE / DELETE).
- For SECURITY DEFINER functions, set
search_path = publicto prevent search-path attacks.
When to run a DeployDoc diagnosis
Right after enabling RLS on a new table, before pushing the migration to production.