Production Checklist for Self-Hosted Supabase: 25 Steps Before Launch

A complete pre-launch checklist for self-hosted Supabase covering security, performance, backups, and operations to ensure a smooth production deployment.

Cover Image for Production Checklist for Self-Hosted Supabase: 25 Steps Before Launch

You've deployed self-hosted Supabase, built your application, and everything works in development. Now comes the moment of truth: going to production. This is where many teams stumble. The GitHub discussions are filled with horror stories—exposed service keys, missing RLS policies, emails that never arrive, and databases that grind to a halt under real traffic.

This checklist distills hard-won lessons from the self-hosting community into 25 essential steps to complete before your first real user signs up. Whether you're an indie hacker launching your SaaS or a team deploying for enterprise compliance, working through this list will save you from the most common production pitfalls.

Security Fundamentals

Security mistakes in production are unforgiving. A single exposed API key or missing RLS policy can compromise your entire database. Start here.

1. Enable Row Level Security on Every Table

This is non-negotiable. Tables without RLS are accessible to anyone with your project URL and anon key—which is designed to be public.

-- Check for tables without RLS enabled
SELECT schemaname, tablename 
FROM pg_tables 
WHERE schemaname = 'public' 
AND tablename NOT IN (
  SELECT tablename FROM pg_tables t
  JOIN pg_policies p ON t.tablename = p.tablename
);

Run this query and enable RLS on every result. Then add appropriate policies. The deny-by-default approach means no access until you explicitly grant it.

2. Audit Your RLS Policies

Enabled RLS with weak policies is almost as dangerous as no RLS. Common mistakes include:

  • Policies that use auth.uid() without checking if the user actually owns the data
  • Overly broad SELECT policies that leak data
  • Missing policies for INSERT, UPDATE, or DELETE operations

Test each policy with different user roles. Supascale's security guide covers RLS patterns in depth.

3. Verify API Key Separation

Your self-hosted Supabase has two keys:

  • anon key: Safe to expose in frontend code, scoped by RLS
  • service role key: Full database access, bypasses RLS

Grep your codebase for the service role key. It should only appear in server-side code, never in:

  • Frontend bundles
  • Mobile apps
  • Environment variables exposed to browsers
  • Git history (use git log -p | grep "service_role")

4. Regenerate Default Secrets

The default docker-compose.yml ships with placeholder secrets. If you haven't already, regenerate all of these:

# Generate new secrets
openssl rand -base64 32  # JWT_SECRET
openssl rand -base64 32  # ANON_KEY (generate proper JWT)
openssl rand -base64 32  # SERVICE_ROLE_KEY (generate proper JWT)
openssl rand -base64 32  # DASHBOARD_PASSWORD
openssl rand -base64 32  # POSTGRES_PASSWORD

Our environment variables guide explains each secret and how to generate production-ready values.

5. Enable SSL for Database Connections

Even on internal networks, enforce SSL for PostgreSQL connections:

-- Force SSL for all connections
ALTER SYSTEM SET ssl = on;
ALTER SYSTEM SET ssl_cert_file = '/path/to/server.crt';
ALTER SYSTEM SET ssl_key_file = '/path/to/server.key';

Then restrict non-SSL connections in pg_hba.conf:

hostssl all all 0.0.0.0/0 scram-sha-256

Email and Authentication

Authentication failures in production create terrible user experiences. These are the issues that generate angry support tickets.

6. Configure Production SMTP

Supabase's built-in email has a rate limit of just 2 emails per hour and no delivery guarantees. This works for testing, not production.

Configure a real SMTP provider (Resend, Postmark, SendGrid, or Amazon SES) by setting these environment variables:

GOTRUE_SMTP_HOST=smtp.your-provider.com
GOTRUE_SMTP_PORT=587
GOTRUE_SMTP_USER=your-smtp-user
GOTRUE_SMTP_PASS=your-smtp-password
GOTRUE_SMTP_SENDER_NAME="Your App"

Send test emails to multiple providers (Gmail, Outlook, Yahoo) to verify deliverability.

7. Customize Email Templates

Default Supabase emails look generic. Customize them for your brand and ensure:

  • Magic link URLs point to your production domain
  • Confirmation emails have clear CTAs
  • Password reset instructions are accurate

Our SMTP configuration guide walks through the full setup process.

8. Set Appropriate Rate Limits

The default GOTRUE_RATE_LIMIT_EMAIL_SENT of 30 per hour might be too low for a product launch. Coordinate with your SMTP provider on appropriate limits:

# Adjust based on your provider's allowances
GOTRUE_RATE_LIMIT_EMAIL_SENT=100

But be careful—email providers dislike sudden spikes. If you're planning a big launch, implement a waitlist instead of direct signups.

9. Configure OAuth Providers

If you're using social login, verify each provider's callback URLs point to your production domain. A common mistake is leaving localhost callbacks in place.

Check the OAuth setup guide and verify:

  • Google OAuth credentials are production-ready (not "Testing" mode)
  • GitHub OAuth callback URL is correct
  • All redirect URLs use HTTPS

10. Enable MFA for Admin Accounts

At minimum, enable multi-factor authentication for any account with elevated privileges. Consider requiring MFA for all users depending on your security requirements.

Database Performance

Production databases behave differently under real load. These optimizations prevent the dreaded "it worked in development" scenario.

11. Add Indexes for Common Queries

Analyze your application's query patterns and ensure proper indexes exist:

-- Find slow queries without indexes
SELECT 
  calls,
  mean_exec_time,
  query 
FROM pg_stat_statements 
ORDER BY mean_exec_time DESC 
LIMIT 20;

Pay special attention to:

  • Foreign key columns used in JOINs
  • Columns used in RLS policy conditions
  • Columns in ORDER BY clauses

Our database indexing guide covers indexing strategies in detail.

12. Configure Connection Pooling

Direct connections exhaust PostgreSQL's connection limit quickly. Configure PgBouncer or Supavisor:

# In your environment configuration
POSTGRES_POOL_MODE=transaction
POSTGRES_MAX_CONNECTIONS=100

See the connection pooling guide for production-ready settings.

13. Tune PostgreSQL for Your Hardware

Default PostgreSQL settings are conservative. Adjust based on your VPS resources:

-- For a 4GB RAM server
ALTER SYSTEM SET shared_buffers = '1GB';
ALTER SYSTEM SET effective_cache_size = '3GB';
ALTER SYSTEM SET work_mem = '64MB';
ALTER SYSTEM SET maintenance_work_mem = '256MB';

The PostgreSQL performance tuning guide provides settings for different server sizes.

14. Enable Query Statistics

Install pg_stat_statements to monitor query performance:

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

This lets you identify slow queries before they become problems.

Backup and Recovery

Backups you haven't tested are just hopes. Make sure you can actually recover.

15. Configure Automated Backups

Set up scheduled database backups to S3-compatible storage. Supascale handles this automatically with one-click configuration, or you can script it yourself:

# Daily backup script
pg_dump $DATABASE_URL | gzip | aws s3 cp - s3://your-bucket/backup-$(date +%Y%m%d).sql.gz

16. Back Up Storage Separately

Your database backup doesn't include files in Supabase Storage. This is a common oversight that leads to data loss. Configure storage backups using:

17. Test Your Restore Process

Actually restore from a backup before going live. Time how long it takes and document the procedure:

  1. Spin up a test instance
  2. Restore the backup
  3. Verify data integrity
  4. Confirm application functionality

Our backup testing guide covers this in depth.

18. Document Recovery Procedures

Write down your disaster recovery steps while they're fresh. Include:

  • Who has access to backups
  • Step-by-step restore commands
  • Expected recovery time
  • Contact information for emergencies

Operations and Monitoring

Production systems need visibility. Set these up before you need them.

19. Configure Health Checks

Ensure all Supabase services report healthy status:

# Check container health
docker compose ps --format "{{.Name}}: {{.Status}}"

Integrate health checks with your monitoring system to catch failures early. The service health monitoring guide covers alerting setup.

20. Set Up Log Aggregation

Production debugging without logs is painful. Configure centralized logging:

# docker-compose.yml addition
services:
  kong:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Or forward logs to your preferred aggregator (Loki, Elasticsearch, CloudWatch). See the log management guide.

21. Configure Resource Alerts

Set alerts for:

  • Disk space < 20% free
  • Memory usage > 85%
  • CPU sustained > 80%
  • Database connection count approaching limit

The monitoring guide explains how to set up Prometheus and Grafana for Supabase.

22. Plan for Updates

Self-hosted Supabase releases roughly monthly. Plan your update strategy:

  • Subscribe to the Supabase changelog
  • Test updates in staging first
  • Schedule maintenance windows
  • Have a rollback plan

The upgrade guide covers zero-downtime updates.

Domain and SSL

Custom domains with proper SSL prevent browser warnings and build user trust.

23. Configure Your Custom Domain

Point your domain to the Supabase API endpoint and configure the reverse proxy. Supascale simplifies this with automatic DNS configuration, or follow our custom domains guide for manual setup.

24. Automate SSL Renewal

Let's Encrypt certificates expire after 90 days. Configure automatic renewal:

# Certbot automatic renewal
certbot renew --quiet --deploy-hook "docker compose restart kong"

Test renewal before going live: certbot renew --dry-run

25. Verify CORS Configuration

Misconfigured CORS causes frustrating client-side errors. Verify your Kong configuration includes your frontend domains:

KONG_CORS_ORIGINS: "https://your-app.com,https://www.your-app.com"

The Final Check

Before flipping the switch, run through this quick verification:

# Security
[ ] RLS enabled on all tables
[ ] Service role key not in frontend code
[ ] All default secrets regenerated
[ ] SSL enforced

# Email/Auth  
[ ] Production SMTP configured and tested
[ ] OAuth callbacks point to production
[ ] Email rate limits appropriate

# Database
[ ] Indexes added for common queries
[ ] Connection pooling configured
[ ] PostgreSQL tuned for hardware

# Backup
[ ] Automated backups configured
[ ] Storage backups configured
[ ] Restore tested and documented

# Operations
[ ] Health checks configured
[ ] Log aggregation set up
[ ] Resource alerts configured
[ ] Update plan documented

# Domain/SSL
[ ] Custom domain configured
[ ] SSL certificates auto-renew
[ ] CORS configured correctly

How Supascale Helps

Managing this checklist manually for every deployment is tedious and error-prone. Supascale automates many of these concerns:

  • Automated backups: Scheduled S3 backups with one-click restore
  • Custom domains: Automatic DNS configuration and free SSL certificates
  • OAuth setup: Visual configuration for all major providers
  • Health monitoring: Built-in service status dashboard
  • One-time pricing: $39.99 for unlimited projects, no monthly fees

Whether you use Supascale or manage everything yourself, completing this checklist before launch will save you from the most common self-hosting disasters.

Further Reading