Production checklist

What to check before launching an app on SupaHost: RLS on every exposed table, key handling, Auth redirect URLs and settings, function auth, tested backups, alerts and capacity.

Work through this list before you send real users to a SupaHost project, and again after big changes. Most items take a minute. The ones in the first two sections protect your data; the rest protect your uptime and your ability to recover.

Data access

1. RLS is enabled on every table in an exposed schema. The Data API exposes public, storage, graphql_public and pgmq_public, and you can't change that list on SupaHost. Any table you create in public is reachable with the anon key unless RLS stops it. Find the gaps:

select c.relname as table_name
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public'
  and c.relkind in ('r', 'p')
  and not c.relrowsecurity;

2. Every RLS-enabled table has policies, or you've decided on purpose that nobody but service_role should reach it. A table with RLS and no policies returns empty results, which is safe but often a bug:

select t.tablename
from pg_tables t
where t.schemaname = 'public'
  and t.rowsecurity
  and not exists (
    select 1 from pg_policies p
    where p.schemaname = t.schemaname and p.tablename = t.tablename
  );

An AI agent connected over MCP can run the same checks with get_advisors, which flags tables with RLS off, tables with no policies, and functions with a mutable search_path.

3. Policies are narrow. Read each using (true) and ask whether every visitor should really see those rows. Prefer to authenticated using ((select auth.uid()) = user_id) for user-owned data. See Row Level Security.

4. Views and functions don't leak around RLS. Views run with their owner's rights unless created with security_invoker = true, and security definer functions bypass RLS. Pin search_path on functions:

alter view public.order_totals set (security_invoker = true);
alter function public.close_booking(uuid) set search_path = '';

See security definer functions and RLS.

5. Storage buckets have the right visibility and policies. Public buckets serve files to anyone with the URL. Private buckets need policies on storage.objects. See Storage access control.

6. Queue functions aren't open by accident. If you call pgmq_public from clients, you've added RLS and grants deliberately. See Queues with pgmq.

Keys and secrets

7. The service_role key is only on servers. Search your client bundles for it after a build:

npm run build
# Prints any built file that contains the key. No output means none do.
# Check the variable is set first, or grep would match every file.
test -n "$SUPABASE_SERVICE_ROLE_KEY" && grep -rlF "$SUPABASE_SERVICE_ROLE_KEY" .next/static dist build 2>/dev/null

8. No keys or tokens are committed to git. .env files are in .gitignore. Function secrets are on the Functions tab, not in source.

9. MCP access tokens are scoped. Use project-scoped, read-only tokens for anything touching production, one per person and tool. Revoke ones that aren't used. See Access tokens.

More in Keeping keys safe.

Auth

10. Site URL is your real app URL. Left unset, it defaults to http://localhost:3000, and confirmation emails will point there. Set it on Auth → Site URL.

11. Redirect URLs are exact. List your production URLs. Keep wildcards narrow (https://*-acme.vercel.app/** rather than https://*.vercel.app/**, which matches anyone's Vercel deployment). See Site URL and redirect URLs.

12. Sign-in settings match your app. Review Auth → Sign-in & security: leave email confirmation on (don't tick "Skip email confirmation" in production), set a minimum password length of at least 8, turn on "Disable new sign-ups" for invite-only apps, and leave anonymous sign-ins off unless you use them.

13. OAuth apps use the right callback. Each provider's console lists https://<your project or custom domain>/auth/v1/callback. If you added a custom domain, update it there too. See Custom domains.

14. Auth emails read well. Set the App name and check each template with "Preview saved version". See Auth emails and templates.

Edge Functions

15. "Require JWT" is on for every function that isn't a public webhook. Remember it accepts the anon key too, so functions acting for a user must check the caller with supabase.auth.getUser(token). See Authentication and JWT verification.

16. Webhooks verify signatures. Functions with "Require JWT" off, such as a Stripe webhook, must check the provider's signature. See Example: Stripe webhooks.

17. CORS allows only your origins for browser-called functions. See CORS for browser calls.

Backups and recovery

18. You've tested a restore. Download a pg_dump and load it into a local Supabase, or restore a non-production project, and time it. See Restoring a project.

19. Storage files are exported on a schedule. They aren't in the daily backups. Use Package files on the Backups tab. See Backups.

20. You know your recovery window. 7 days of daily backups on Builder; 7-day PITR on Pro; 14-day on Business.

Operations

21. Alerts reach someone. Downtime and usage alerts are on, and Also send alerts to includes a shared inbox. See Monitoring, health checks and alerts.

22. Updates are planned. Turn on automatic upgrades with a maintenance window (03:00-05:00 UTC) that suits your traffic, or apply releases yourself.

23. Connections are budgeted. 400 connections are shared by every project on the instance, with no pooler. Keep ORM pools small and close connections in scripts. See Connections and timeouts.

24. Hot queries are indexed. Check slow queries with EXPLAIN ANALYZE and add indexes for columns used in RLS policies. See RLS performance best practices.

25. Capacity fits. Compare your expected busiest hour with the guides on Plans and limits.

Accept the platform's limits

Last, confirm SupaHost's current state is acceptable for this app: public beta, no SLA, no SOC 2, ISO 27001 or HIPAA, single VM per instance, and no key rotation. See Security at SupaHost.

Updated 11 November 2026 · View as Markdown · Something wrong or missing? Tell us