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
SELECTpolicies that leak data - Missing policies for
INSERT,UPDATE, orDELETEoperations
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:
- MinIO replication
- S3 sync to a separate bucket
- Supascale's integrated storage backup feature
17. Test Your Restore Process
Actually restore from a backup before going live. Time how long it takes and document the procedure:
- Spin up a test instance
- Restore the backup
- Verify data integrity
- 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.
