supabase-prod-checklist
Execute Supabase production deployment checklist covering RLS, key hygiene, connection pooling, backups, monitoring, Edge Functions, and Storage policies. Use when deploying to production, preparing for launch, or auditing a live Supabase project for security and performance gaps. Trigger with "supabase production", "supabase go-live", "supabase launch checklist", "supabase prod ready", "deploy supabase", "supabase production readiness".
What this skill does
# Supabase Production Deployment Checklist
## Overview
Actionable 14-step checklist for taking a Supabase project to production. Covers RLS enforcement, key separation, connection pooling (Supavisor), backups/PITR, network restrictions, custom domains, auth emails, rate limits, monitoring, Edge Functions, Storage policies, indexes, and migrations. Based on Supabase's official [production guide](https://supabase.com/docs/guides/deployment/going-into-prod).
## Prerequisites
- Supabase project on Pro plan or higher (required for PITR, network restrictions)
- Separate production project (never share dev/prod)
- `@supabase/supabase-js` v2+ installed
- Supabase CLI installed (`npx supabase --version`)
- Domain and DNS configured for custom domain
- Deployment platform ready (Vercel, Netlify, Cloudflare, etc.)
## Instructions
### Step 1: Enforce Row Level Security on ALL Tables
RLS is the single most critical production requirement. Without it, any client with your anon key can read/write every row.
```sql
-- Audit: find tables WITHOUT RLS enabled
-- This query MUST return zero rows before going live
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public' AND rowsecurity = false;
```
```sql
-- Enable RLS on a table
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
-- Create a basic read policy (authenticated users see own rows)
CREATE POLICY "Users can view own profile"
ON public.profiles
FOR SELECT
USING (auth.uid() = user_id);
-- Create an insert policy
CREATE POLICY "Users can insert own profile"
ON public.profiles
FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Create an update policy
CREATE POLICY "Users can update own profile"
ON public.profiles
FOR UPDATE
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
```
- [ ] RLS enabled on every public table (zero rows from audit query above)
- [ ] SELECT, INSERT, UPDATE, DELETE policies defined for each table
- [ ] Policies tested with both authenticated and anonymous roles
- [ ] No tables use `USING (true)` without intent (public read tables only)
### Step 2: Enforce Key Separation — Anon vs Service Role
The `anon` key is safe for client-side code. The `service_role` key bypasses RLS entirely and must never leave server-side environments.
```typescript
// Client-side — ONLY use anon key
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! // Safe for browsers
);
```
```typescript
// Server-side only — service_role key (API routes, webhooks, cron jobs)
import { createClient } from '@supabase/supabase-js';
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!, // NEVER expose to client
{ auth: { autoRefreshToken: false, persistSession: false } }
);
```
- [ ] Anon key used in all client-side code (`NEXT_PUBLIC_` prefix)
- [ ] Service role key used only in server-side code (API routes, Edge Functions)
- [ ] Service role key not in any client bundle (verify with `grep -r "service_role" dist/`)
- [ ] Database password changed from the auto-generated default
### Step 3: Configure Connection Pooling (Supavisor)
Supabase uses Supavisor for connection pooling. Serverless functions (Vercel, Netlify, Cloudflare Workers) MUST use the pooled connection string to avoid exhausting the database connection limit.
```
# Direct connection (migrations, admin tasks only)
postgresql://postgres:[PASSWORD]@db.[REF].supabase.co:5432/postgres
# Pooled connection via Supavisor (application code — USE THIS)
# Port 6543 = Supavisor pooler (vs 5432 direct)
postgresql://postgres.[REF]:[PASSWORD]@aws-0-us-east-1.pooler.supabase.com:6543/postgres
```
```typescript
// For serverless environments — use pooled connection
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!,
{
db: { schema: 'public' },
// Supavisor handles pooling at port 6543
// No need to configure pgBouncer settings in the client
}
);
```
- [ ] Application code uses pooled connection string (port 6543)
- [ ] Direct connection reserved for migrations and admin tasks only
- [ ] Connection string in deployment platform env vars (not hardcoded)
- [ ] Verified pool mode: `transaction` for serverless, `session` for long-lived connections
### Step 4: Enable Database Backups
Supabase provides automatic daily backups on Pro plan. Point-in-time recovery (PITR) enables granular restores.
- [ ] Automatic daily backups enabled (Pro plan — verify in Dashboard > Database > Backups)
- [ ] Point-in-time recovery configured (Dashboard > Database > Backups > PITR)
- [ ] Tested restore procedure on a staging project (do not skip this)
- [ ] Migration files committed to version control (`supabase/migrations/` directory)
- [ ] `npx supabase db push` tested against a fresh project to verify migrations replay cleanly
### Step 5: Configure Network Restrictions
Restrict database access to known IP addresses. This prevents unauthorized direct database connections even if credentials leak.
- [ ] IP allowlist configured (Dashboard > Database > Network Restrictions)
- [ ] Only deployment platform IPs and team office IPs are allowed
- [ ] Verified that application still connects after restrictions applied
- [ ] Documented which IPs are allowed and why
### Step 6: Configure Custom Domain
A custom domain replaces the default `*.supabase.co` URLs with your brand domain for API and auth endpoints.
- [ ] Custom domain configured (Dashboard > Settings > Custom Domains)
- [ ] DNS CNAME record added and verified
- [ ] SSL certificate provisioned and active
- [ ] Application code updated to use custom domain URL
- [ ] OAuth redirect URLs updated to use custom domain
### Step 7: Customize Auth Email Templates
Default Supabase auth emails show generic branding. Customize them so users see your domain and brand.
- [ ] Confirmation email template customized (Dashboard > Auth > Email Templates)
- [ ] Password reset email template customized
- [ ] Magic link email template customized
- [ ] Invite email template customized
- [ ] Custom SMTP configured (Dashboard > Auth > SMTP Settings) — avoids rate limits and improves deliverability
- [ ] Email confirmation enabled (Dashboard > Auth > Settings)
- [ ] OAuth redirect URLs restricted to production domains only
- [ ] Unused auth providers disabled
### Step 8: Understand Rate Limits Per Tier
Supabase enforces rate limits that vary by plan. Hitting these in production causes 429 errors.
| Resource | Free | Pro | Team |
|----------|------|-----|------|
| API requests | 500/min | 1,000/min | 5,000/min |
| Auth emails | 4/hour | 30/hour | 100/hour |
| Realtime connections | 200 concurrent | 500 concurrent | 2,000 concurrent |
| Edge Function invocations | 500K/month | 2M/month | 5M/month |
| Storage bandwidth | 2GB/month | 250GB/month | Custom |
| Database size | 500MB | 8GB | 50GB |
- [ ] Rate limits documented for your plan tier
- [ ] Client-side retry logic with exponential backoff for 429 responses
- [ ] Auth email rate limits understood (use custom SMTP to increase)
- [ ] Realtime connection limits planned for expected concurrent users
### Step 9: Review Monitoring Dashboards
Supabase provides built-in monitoring. Review these before launch to establish baselines.
```typescript
// Health check endpoint — deploy this to your application
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
);
export async function GET() {
const start = Date.now();
const { data, error } = await supabase
.from('_health_check') // Create a small table for this
.select('id')
.limit(1);
const latency = Date.now() - start;
return Response.json({
status: error ? 'unhealthy' : 'healthy',
latency_ms: latency,
timestamp: new Date().toISRelated in Cloud & DevOps
appbuilder-action-scaffolder
IncludedCreate, implement, deploy, and debug Adobe Runtime actions with consistent layout, validation, and error handling. Use this skill whenever the user needs to add actions to an App Builder project, understand action structure (params, response format, web/raw actions), configure actions in the manifest, use App Builder SDKs (State, Files, Events, database), deploy and invoke actions via CLI, debug action issues, or implement patterns such as webhook receivers, custom event providers, journaling consumers, large payload redirects, action sequence pipelines, and Asset Compute workers. Also trigger when users mention serverless functions in Adobe context, action logging, IMS authentication for actions, or cron-style scheduled actions.
orchestrating-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. Use this skill when the user needs a multi-step Data Cloud pipeline, cross-phase troubleshooting, or data space and data kit management. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase sf data360 workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching phase-specific skill), the task is STDM/session tracing/parquet telemetry (use observing-agentforce), standard CRM SOQL (use querying-soql), or Apex implementation (use generating-apex).
github-project-automation
IncludedAutomate GitHub repository setup with CI/CD workflows, issue templates, Dependabot, and CodeQL security scanning. Includes 12 production-tested workflows and prevents 18 errors: YAML syntax, action pinning, and configuration. Use when: setting up GitHub Actions CI/CD, creating issue/PR templates, enabling Dependabot or CodeQL scanning, deploying to Cloudflare Workers, implementing matrix testing, or troubleshooting YAML indentation, action version pinning, secrets syntax, runner versions, or CodeQL configuration. Keywords: github actions, github workflow, ci/cd, issue templates, pull request templates, dependabot, codeql, security scanning, yaml syntax, github automation, repository setup, workflow templates, github actions matrix, secrets management, branch protection, codeowners, github projects, continuous integration, continuous deployment, workflow syntax error, action version pinning, runner version, github context, yaml indentation error
sf-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase `sf data360` workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching sf-datacloud-* skill), the task is STDM/session tracing/parquet telemetry (use sf-ai-agentforce-observability), standard CRM SOQL (use sf-soql), or Apex implementation (use sf-apex).
fabric-cli
IncludedUse this skill for Fabric.so CLI workflows with the `fabric` terminal command: diagnose/install/login, search or browse a Fabric library, save notes/links/files, create folders, ask the Fabric AI assistant, manage tasks/workspaces, generate shell completion, check subscription usage, produce JSON output, and use Fabric as persistent agent memory. Do not use for Microsoft Fabric/Azure/Power BI `fab`, Daniel Miessler's Fabric framework, Python Fabric SSH, Fabric.js, or textile/fashion fabric.
lark
IncludedLark/Feishu CLI skills: lark-cli operations for docs, markdown, sheets, base, calendar, im, mail, task, okr, drive, wiki, slides, whiteboard, apps, approval, attendance, contact, vc, minutes, event. Use when the user needs to operate Lark/Feishu resources via lark-cli, send messages, manage documents, spreadsheets, calendars, tasks, OKRs, deploy web pages, or any Feishu/Lark workspace operations.