This guide walks you through migrating your IgnitionStack project from Supabase Cloud to a self-hosted Supabase instance on your own VPS.
| Benefit | Description |
|---|---|
| Full Control | Complete ownership of your infrastructure and data |
| Cost Effective | Fixed monthly costs vs. usage-based pricing at scale |
| Data Sovereignty | Keep data in specific regions for compliance |
| Customization | Full access to PostgreSQL and all services |
| No Vendor Lock-in | Freedom to migrate anywhere |
Before starting the migration:
# Recommended plan: KVM 2
# - 8 GB RAM
# - 4 vCPU
# - 100 GB NVMe SSD
# - ~$13.99/month| Provider | Plan | RAM | Storage | Price/mo |
|---|---|---|---|---|
| DigitalOcean | Droplet | 4GB | 80GB SSD | $24 |
| Hetzner | CX31 | 8GB | 80GB SSD | €8.49 |
| Vultr | High Frequency | 4GB | 128GB NVMe | $24 |
| Linode | Dedicated 4GB | 4GB | 80GB SSD | $36 |
| Contabo | VPS M | 16GB | 400GB SSD | €10.99 |
ssh root@your-server-ip# Ubuntu/Debian
apt update && apt upgrade -y
# Install essential packages
apt install -y curl git ufw# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# Install Docker Compose
apt install -y docker-compose-plugin
# Verify installation
docker --version
docker compose versionufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw allow 5432 # PostgreSQL (optional, for external access)
ufw enable# Create directory
mkdir -p /opt/supabase
cd /opt/supabase
# Clone the official Supabase Docker setup
git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker# Copy example env
cp .env.example .env
# Generate secure keys
# JWT Secret (must be at least 32 characters)
JWT_SECRET=$(openssl rand -base64 32)
echo "JWT_SECRET=$JWT_SECRET"
# Anon Key
ANON_KEY=$(docker run --rm supabase/gotrue:latest sh -c "echo '{\"role\":\"anon\",\"iss\":\"supabase\",\"iat\":'$(date +%s)',\"exp\":'$(($(date +%s) + 315360000))'}' | base64 -w 0")
echo "ANON_KEY=$ANON_KEY"
# Service Role Key
SERVICE_ROLE_KEY=$(docker run --rm supabase/gotrue:latest sh -c "echo '{\"role\":\"service_role\",\"iss\":\"supabase\",\"iat\":'$(date +%s)',\"exp\":'$(($(date +%s) + 315360000))'}' | base64 -w 0")
echo "SERVICE_ROLE_KEY=$SERVICE_ROLE_KEY"nano .envUpdate these critical values:
############
# Secrets
############
POSTGRES_PASSWORD=your-super-secure-password-here
JWT_SECRET=your-generated-jwt-secret
ANON_KEY=your-generated-anon-key
SERVICE_ROLE_KEY=your-generated-service-role-key
############
# Dashboard
############
DASHBOARD_USERNAME=admin
DASHBOARD_PASSWORD=your-dashboard-password
############
# URLs
############
SITE_URL=https://your-domain.com
API_EXTERNAL_URL=https://api.your-domain.com
SUPABASE_PUBLIC_URL=https://api.your-domain.com
############
# SMTP (for auth emails)
############
SMTP_HOST=smtp.resend.com
SMTP_PORT=465
SMTP_USER=resend
SMTP_PASS=your-resend-api-key
SMTP_SENDER_NAME=Your App
SMTP_ADMIN_EMAIL=admin@your-domain.comdocker compose up -d
# Check status
docker compose ps
# View logs
docker compose logs -fapt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update
apt install caddynano /etc/caddy/Caddyfile# API and Studio
api.your-domain.com {
reverse_proxy localhost:8000
}
# Studio Dashboard
studio.your-domain.com {
reverse_proxy localhost:3000
}
# Storage/S3
storage.your-domain.com {
reverse_proxy localhost:5000
}systemctl reload caddy# Install Supabase CLI locally
npm install -g supabase
# Login to Supabase
supabase login
# Link to your cloud project
supabase link --project-ref your-project-ref
# Dump the database
supabase db dump -f supabase_backup.sql --data-only# Copy backup to server
scp supabase_backup.sql root@your-server-ip:/opt/supabase/
# Import to self-hosted PostgreSQL
docker exec -i supabase-db psql -U postgres -d postgres < /opt/supabase/supabase_backup.sql# Download files from Supabase Cloud Storage using their API
# Or use the Supabase dashboard to download and re-upload
# Using s3cmd or rclone for bulk transfer
rclone sync supabase-cloud:bucket-name self-hosted:bucket-name# .env.production
NEXT_PUBLIC_SUPABASE_URL=https://api.your-domain.com
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-self-hosted-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-self-hosted-service-role-keyIn your self-hosted Supabase dashboard:
https://your-app-domain.comhttps://your-app-domain.com/**https://your-app-domain.com/auth/callback# From your development machine
npm run dev
# Test database connection
npm run db:gen# Create backup script
cat > /opt/supabase/backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR=/opt/supabase/backups
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
docker exec supabase-db pg_dump -U postgres postgres > $BACKUP_DIR/backup_$DATE.sql
# Keep only last 7 days
find $BACKUP_DIR -name "backup_*.sql" -mtime +7 -delete
EOF
chmod +x /opt/supabase/backup.sh
# Add to crontab (daily at 2 AM)
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/supabase/backup.sh") | crontab -# Install Netdata for real-time monitoring
bash <(curl -Ss https://my-netdata.io/kickstart.sh)cat > /etc/logrotate.d/supabase << EOF
/opt/supabase/supabase/docker/volumes/logs/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
}
EOFContainer won’t start:
docker compose logs [service-name]
docker compose down && docker compose up -dDatabase connection refused:
# Check if PostgreSQL is running
docker exec supabase-db pg_isready
# Check ports
netstat -tlnp | grep 5432Auth emails not sending:
# Test SMTP connection
docker exec supabase-auth /bin/sh -c "echo test | mail -s 'Test' your@email.com"Studio not accessible:
# Check if Kong is routing correctly
curl -i http://localhost:8000/rest/v1/# Update Supabase
cd /opt/supabase/supabase/docker
git pull
docker compose pull
docker compose up -d
# View realtime logs
docker compose logs -f
# Restart specific service
docker compose restart [service-name]
# Full restart
docker compose down && docker compose up -d