Self-Host Supabase: Complete Docker Deployment Guide 2026
Self-Host Supabase: Complete Docker Deployment Guide 2026
Supabase has become the default Firebase alternative for developers who want PostgreSQL over NoSQL. The hosted version is excellent, but it comes with pricing that scales with usage -- and your data lives on their infrastructure.
Self-hosting Supabase gives you:
- Zero marginal cost for database operations
- Complete data ownership on your infrastructure
- No usage limits on API calls, storage, or bandwidth
- Compliance for HIPAA, GDPR, and data residency requirements
This guide walks through a production-ready Docker deployment.
Prerequisites
- A VPS with at least 4GB RAM (8GB recommended for production)
- Docker and Docker Compose installed
- A domain name pointed to your server
- Basic familiarity with PostgreSQL
Recommended providers: Hetzner, DigitalOcean, Vultr, or any VPS with good I/O performance (PostgreSQL is disk-intensive).
Quick Start
Clone the official self-hosted repository:
git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker
cp .env.example .envConfiguration
Edit the .env file with your settings:
# Generate secure secrets
POSTGRES_PASSWORD=$(openssl rand -base64 32)
JWT_SECRET=$(openssl rand -base64 32)
ANON_KEY=$(openssl rand -base64 32)
SERVICE_ROLE_KEY=$(openssl rand -base64 32)
# Your domain
SITE_URL=https://your-domain.com
API_EXTERNAL_URL=https://your-domain.com
# SMTP for auth emails (optional but recommended)
SMTP_HOST=smtp.your-provider.com
SMTP_PORT=587
SMTP_USER=your-smtp-user
SMTP_PASS=your-smtp-password
SMTP_SENDER_NAME=Your AppCritical Security Settings
Never use the example secrets in production. Generate new ones:
# Generate all secrets at once
echo "POSTGRES_PASSWORD=$(openssl rand -base64 32)"
echo "JWT_SECRET=$(openssl rand -base64 32)"
echo "ANON_KEY=$(supabase gen keys --type anon)"
echo "SERVICE_ROLE_KEY=$(supabase gen keys --type service_role)"Deploy
Start all services:
docker compose up -dThis launches:
- PostgreSQL -- your primary database
- GoTrue -- authentication service
- PostgREST -- automatic REST API from your schema
- Realtime -- websocket subscriptions for live data
- Storage -- S3-compatible file storage
- Kong -- API gateway
- Studio -- the Supabase dashboard
Verify the Deployment
Check all containers are running:
docker compose psAccess the dashboard at http://your-server-ip:3000 (or your domain if configured).
Add SSL with Caddy
For production, add a reverse proxy with automatic SSL:
# Install Caddy
sudo apt install -y caddy
# Configure Caddy
sudo tee /etc/caddy/Caddyfile << EOF
your-domain.com {
reverse_proxy localhost:8000
}
EOF
# Restart Caddy
sudo systemctl restart caddyCaddy automatically obtains and renews Let's Encrypt certificates.
Connect Your Application
Update your Supabase client to point to your self-hosted instance:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
'https://your-domain.com',
'your-anon-key'
)Everything else works identically to hosted Supabase -- auth, database queries, realtime subscriptions, and storage.
Database Backups
Set up automated PostgreSQL backups:
# Create backup script
cat > /opt/supabase-backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR=/opt/backups
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
docker exec supabase-db pg_dump -U postgres postgres > $BACKUP_DIR/supabase_$TIMESTAMP.sql
# Keep only last 7 days
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete
EOF
chmod +x /opt/supabase-backup.sh
# Add to crontab (daily at 2am)
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/supabase-backup.sh") | crontab -Performance Tuning
For production workloads, tune PostgreSQL in docker-compose.yml:
db:
command:
- postgres
- -c
- shared_buffers=1GB
- -c
- effective_cache_size=3GB
- -c
- work_mem=64MB
- -c
- maintenance_work_mem=512MBAdjust based on your server's RAM. General rule: shared_buffers = 25% of RAM.
Monitoring
Add basic monitoring with the built-in PostgreSQL stats:
-- Check active connections
SELECT count(*) FROM pg_stat_activity;
-- Check table sizes
SELECT relname, pg_size_pretty(pg_total_relation_size(relid))
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
-- Check slow queries (enable pg_stat_statements first)
SELECT query, calls, mean_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;For comprehensive monitoring, consider adding Grafana + Prometheus to your stack.
Updating Supabase
Pull the latest images and restart:
cd /path/to/supabase/docker
git pull
docker compose pull
docker compose up -dAlways backup your database before updating.
Common Issues
Auth emails not sending
- Verify SMTP credentials in
.env - Check the GoTrue container logs:
docker logs supabase-auth - Test SMTP connectivity from the server
Realtime not connecting
- Ensure websocket ports are open in your firewall
- Check Kong configuration for websocket proxying
- Verify
REALTIME_URLin your client matches your server
Slow queries
- Add indexes based on your query patterns
- Enable
pg_stat_statementsto identify bottlenecks - Consider connection pooling with PgBouncer for high traffic
Cost Comparison
| Setup | Monthly Cost | Storage | API Calls |
|---|---|---|---|
| Supabase Pro | $25+ | 8GB included | 100K included |
| Self-Hosted (4GB VPS) | ~$20 | Unlimited | Unlimited |
| Self-Hosted (8GB VPS) | ~$40 | Unlimited | Unlimited |
Self-hosting breaks even almost immediately and scales linearly with your VPS cost, not your usage.
Next Steps
- Set up automated backups to S3 for disaster recovery
- Add monitoring with Grafana for visibility
- Configure Cloudflare or Caddy for SSL and DDoS protection
- Review the Supabase security checklist
Self-hosted Supabase gives you the same developer experience as the hosted version with full control over your data and zero usage-based pricing. For teams processing significant data or with compliance requirements, it is the clear choice.
Compare Supabase alternatives like Appwrite and PocketBase if you want to evaluate other Backend-as-a-Service options.