G: Are you looking to take full control of your Supabase stack? While Supabase’s managed cloud service is incredibly convenient, self-hosting offers unparalleled flexibility, cost savings, and data sovereignty. This guide will walk you through setting up your very own Supabase instance using Docker Compose, from the initial installation to configuring custom API domains for a production-ready environment. Let’s dive in! 🚀
1. Why Self-Host Supabase? The Benefits You Can’t Ignore 🤔
Before we get our hands dirty with installation, let’s explore why self-hosting Supabase might be the perfect solution for your project:
- Complete Control & Customization 🛠️: You have full access to every component of the Supabase stack. This means you can tweak Postgres configurations, integrate with specific authentication providers, or even modify the backend services to fit unique requirements.
- Cost Optimization 💰: For projects with high usage or specific resource needs, self-hosting can often be significantly more cost-effective than a managed service, especially as your application scales. You pay for the underlying infrastructure, not a premium for management.
- Data Sovereignty & Compliance 🔒: Certain industries or regulations require data to reside in specific geographical locations or within a private infrastructure. Self-hosting gives you the power to choose where your data lives, helping you meet strict compliance standards.
- Performance Tuning ⚡: With direct access to the database and services, you can fine-tune performance settings, optimize queries, and ensure your backend is running at peak efficiency for your specific workload.
- Learning Opportunity 🧑💻: Setting up and managing a complex stack like Supabase is a fantastic learning experience. You’ll gain valuable insights into Docker, networking, database administration, and server management.
2. Prerequisites: Gearing Up for Installation ⚙️
Before you begin, ensure your server or local machine meets these requirements:
- Operating System: A Linux-based OS (Ubuntu, Debian, CentOS, etc.) is highly recommended for production environments. macOS or Windows (with WSL2) can work for local development.
- Hardware Resources:
- CPU: At least 2 Cores. For production, 4+ Cores are recommended.
- RAM: Minimum 4GB, preferably 8GB+ for production to handle PostgreSQL efficiently.
- Storage: At least 50GB SSD. More if you anticipate storing large files or extensive database data. SSDs are crucial for database performance.
- Software Installed:
- Docker: The containerization platform.
- Installation Hint (Ubuntu):
sudo apt-get update && sudo apt-get install docker.io -y
- Installation Hint (Ubuntu):
- Docker Compose: A tool for defining and running multi-container Docker applications.
- Installation Hint (Ubuntu):
sudo apt-get install docker-compose -y
- Installation Hint (Ubuntu):
- Git: To clone the Supabase repository.
- Installation Hint (Ubuntu):
sudo apt-get install git -y
- Installation Hint (Ubuntu):
- Docker: The containerization platform.
- Domain Name (Optional but Recommended for Production): If you plan to expose your Supabase instance to the internet, a domain name (e.g.,
api.yourdomain.com
) is essential for proper API routing and SSL certificates.- DNS Setup: You’ll need an
A record
pointing your chosen subdomain (e.g.,api.yourdomain.com
) to your server’s public IP address.
- DNS Setup: You’ll need an
3. Step-by-Step Installation with Docker Compose 🚀
This section will guide you through getting the core Supabase services up and running.
3.1. Clone the Supabase Repository
First, clone the official Supabase supabase/supabase
repository, which contains the Docker Compose configurations and scripts.
git clone --depth 1 https://github.com/supabase/supabase
cd supabase
3.2. Configure Environment Variables (.env
)
The Supabase services rely on a .env
file for crucial configurations like secrets and URLs.
-
Copy the example file:
cp .env.example .env
-
Edit the
.env
file: Open the newly created.env
file with your preferred text editor (e.g.,nano .env
orvim .env
). You’ll need to generate and set the following:JWT_SECRET
: A highly secure, random string used for signing JWTs for authentication.- How to generate:
openssl rand -base64 32
(copy the output). - Example:
JWT_SECRET="YOUR_VERY_STRONG_AND_RANDOM_SECRET_KEY_HERE"
- How to generate:
ANON_KEY
: The public (client-side) key for your Supabase project. This key is embedded in your client applications.- How to generate: Copy your
JWT_SECRET
and paste it here for simplicity, though in production, you might want to use a different (but equally strong) key if you implement custom API key management. - Example:
ANON_KEY="YOUR_PUBLIC_KEY_SAME_AS_JWT_SECRET_FOR_NOW"
- How to generate: Copy your
SERVICE_ROLE_KEY
: The private (server-side) key for your Supabase project. This key grants full access to your database and should never be exposed in client-side code.- How to generate: Copy your
JWT_SECRET
and paste it here. - Example:
SERVICE_ROLE_KEY="YOUR_SUPER_SECRET_KEY_ONLY_FOR_BACKEND"
- How to generate: Copy your
DB_PASSWORD
: The password for your Postgres database user (postgres
). Choose a strong, random password.- Example:
DB_PASSWORD="YourSuperSecureDBPassword123"
- Example:
POSTGRES_PASSWORD
: Alias forDB_PASSWORD
. Make sure it’s the same.PUBLIC_URL
: This is crucial for authentication flows, especially for magic links, password resets, and email confirmations. If you’re using a custom domain (recommended), set it here. If not, use your server’s IP address.- Example (with custom domain):
PUBLIC_URL="https://api.yourdomain.com"
- Example (local/IP):
PUBLIC_URL="http://YOUR_SERVER_IP:8000"
(adjust port if not using a proxy later)
- Example (with custom domain):
NEXT_PUBLIC_SUPABASE_URL
: The URL your client applications will use to connect to Supabase. This should match yourPUBLIC_URL
.NEXT_PUBLIC_SUPABASE_ANON_KEY
: This should match yourANON_KEY
.
Example
.env
snippet:# Required for auth and storage JWT_SECRET="a_very_long_and_random_string_from_openssl_rand_base64_32" ANON_KEY="the_same_long_random_string_as_jwt_secret" SERVICE_ROLE_KEY="the_same_long_random_string_as_jwt_secret" # Database connection (internal to Docker network) DB_PASSWORD="YourStrongDBPasswordHere!" POSTGRES_PASSWORD="${DB_PASSWORD}" # Public URLs for your Supabase instance PUBLIC_URL="https://api.yourdomain.com" # Or http://YOUR_SERVER_IP:8000 NEXT_PUBLIC_SUPABASE_URL="${PUBLIC_URL}" NEXT_PUBLIC_SUPABASE_ANON_KEY="${ANON_KEY}" # Other configurations (storage, realtime, functions, etc. - usually fine as default initially) # ...
Remember to replace the placeholder values with your actual generated secrets and chosen URLs!
3.3. Start the Supabase Services
Once your .env
file is configured, you can bring up all the Supabase services using Docker Compose. The -d
flag runs them in detached mode (in the background).
docker-compose up -d
This command will download all necessary Docker images and start the containers. This might take a while on the first run, depending on your internet connection.
3.4. Verify Installation
After the services start, you can check their status:
-
List running containers:
docker-compose ps
You should see containers for
db
,kong
,realtime
,auth
,storage
,rest
, and possibly others, all in arunning
state. -
View logs for specific services (useful for debugging):
docker-compose logs -f kong # To see the API gateway logs docker-compose logs -f auth # To see authentication service logs docker-compose logs -f db # To see PostgreSQL logs
-
Access the Supabase Studio (Dashboard): By default, the Supabase Studio (dashboard) runs on port
3000
. You can access it in your browser:- If running locally:
http://localhost:3000
- If running on a server:
http://YOUR_SERVER_IP:3000
You’ll be prompted to log in. Use
supabase
as the email and yourDB_PASSWORD
from the.env
file as the password.🎉 Congratulations! Your core Supabase instance is now up and running!
- If running locally:
4. Securing Your API Endpoints with a Custom Domain (Production Ready) 🌐🔒
Directly exposing your services on different ports (like Kong on 8000, Auth on 9999, etc.) is not ideal for production. A reverse proxy (like Nginx) allows you to route all traffic through a single custom domain on standard HTTP/S ports (80/443) and add SSL encryption.
This section assumes you have a domain name (e.g., api.yourdomain.com
) pointing to your server’s IP address.
4.1. Install Nginx & Certbot
Nginx will be our reverse proxy, and Certbot will automate SSL certificate generation from Let’s Encrypt.
sudo apt update
sudo apt install nginx -y
sudo apt install certbot python3-certbot-nginx -y
4.2. Configure Nginx for Supabase
-
Create a new Nginx configuration file for your Supabase domain. Replace
api.yourdomain.com
with your actual domain.sudo nano /etc/nginx/sites-available/supabase.conf
-
Paste the following configuration. This routes requests to the correct internal Docker services.
server { listen 80; listen [::]:80; server_name api.yourdomain.com; # IMPORTANT: Replace with your actual domain # Redirect HTTP to HTTPS (Certbot will add this automatically later) # return 301 https://$host$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name api.yourdomain.com; # IMPORTANT: Replace with your actual domain # SSL Configuration (Certbot will add these automatically) # ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem; # ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem; # include /etc/letsencrypt/options-ssl-nginx.conf; # ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # Proxy requests to Supabase services location / { proxy_pass http://localhost:8000; # Kong (API Gateway) proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900; # Increase timeout for large uploads/downloads } # Specific routes for Auth and Storage (optional, but good for clarity/future routing) location /auth/v1/ { proxy_pass http://localhost:9999; # Supabase Auth proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /storage/v1/ { proxy_pass http://localhost:5000; # Supabase Storage proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 50M; # Max upload size, adjust as needed } location /realtime/v1/ { proxy_pass http://localhost:4000; # Supabase Realtime proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
Explanation of
proxy_pass
destinations:localhost:8000
: This is the default port forkong
(Supabase’s API Gateway), which routes requests to the appropriate backend services.localhost:9999
: Direct access to theauth
service.localhost:5000
: Direct access to thestorage
service.localhost:4000
: Direct access to therealtime
service.
Note: Supabase services run within Docker’s internal network. When you expose a port (e.g.,
8000:8000
), it maps the container’s internal port8000
to the host machine’s port8000
. Nginx on the host then proxies to these exposed ports (localhost:8000
, etc.). -
Enable the Nginx configuration:
sudo ln -s /etc/nginx/sites-available/supabase.conf /etc/nginx/sites-enabled/ sudo nginx -t # Test Nginx configuration for syntax errors sudo systemctl restart nginx # Restart Nginx to apply changes
4.3. Generate SSL Certificates with Certbot
Now, let’s get that crucial HTTPS encryption!
sudo certbot --nginx -d api.yourdomain.com # Replace with your domain
Follow the prompts. Certbot will automatically modify your Nginx configuration to include the SSL certificate paths and set up automatic renewals.
4.4. Update Supabase .env
for Public URL
After setting up your custom domain and SSL, it’s vital to update your Supabase .env
file to reflect these changes.
-
Edit your
.env
file again:nano .env
-
Update
PUBLIC_URL
and related variables:PUBLIC_URL="https://api.yourdomain.com" # Use your actual domain with HTTPS NEXT_PUBLIC_SUPABASE_URL="${PUBLIC_URL}" # NEXT_PUBLIC_SUPABASE_ANON_KEY and SERVICE_ROLE_KEY remain the same
-
Restart Supabase services for the changes to take effect:
docker-compose down docker-compose up -d
Now, your Supabase instance should be accessible via
https://api.yourdomain.com
! Test it by pointing your application’sSUPABASE_URL
to this new domain.
5. Post-Installation & Maintenance Essentials 🔧
Your Supabase instance is running, but responsible self-hosting involves ongoing tasks.
5.1. Accessing the Supabase Studio (Dashboard)
You can now access your Supabase Studio via your custom domain if you configured Nginx to proxy port 3000 to a subdomain (e.g., studio.yourdomain.com
). If not, you can still access it via http://YOUR_SERVER_IP:3000
.
- Login:
email: supabase
,password: YOUR_DB_PASSWORD
(from your.env
). - Initial Setup: Once logged in, you can create a new project (which points to your existing self-hosted database) and start managing tables, users, and storage.
5.2. Backups! 💾
This is arguably the most critical aspect of self-hosting. NEVER skip backups!
- Database (Postgres):
- Use
pg_dump
to regularly dump your database. - Example:
docker exec -t supabase_db_1 pg_dumpall -c -U postgres > backup.sql
- Automate this with cron jobs.
- Store backups securely in a separate location (e.g., S3, Google Cloud Storage, another server).
- Use
- Supabase Configuration: Backup your
.env
file and Nginx configuration. - Storage (S3/MinIO): If you’re using MinIO (the default self-hosted storage) and storing files, ensure you back up the MinIO data directory.
5.3. Updates ⬆️
Supabase is constantly evolving. Staying updated is important for security patches, bug fixes, and new features.
- Stop services:
docker-compose down
- Pull latest changes:
git pull origin main
- Rebuild/Update Docker images:
docker-compose pull
- Start services:
docker-compose up -d
- Note: Always check the official Supabase
supabase/supabase
repository’s release notes for breaking changes before updating a production instance.
- Note: Always check the official Supabase
5.4. Monitoring 📈
Keep an eye on your server’s resources (CPU, RAM, Disk I/O) and Docker container logs. Tools like htop
, docker stats
, Prometheus/Grafana, or cloud-specific monitoring services can be invaluable.
6. Potential Challenges & Troubleshooting Tips 💡
Self-hosting can be tricky, but here are some common issues and how to tackle them:
- Port Conflicts: If a port is already in use (e.g., 80, 443, 3000, 8000),
docker-compose up
will fail.- Solution: Use `sudo netstat -tulnp | grep
` to identify the process using the port and stop it, or change the port mapping in `docker-compose.yml`.
- Solution: Use `sudo netstat -tulnp | grep
- Incorrect Environment Variables: A misspelled
JWT_SECRET
orPUBLIC_URL
can lead to authentication errors, broken magic links, or clients unable to connect.- Solution: Double-check your
.env
file carefully. Remember todocker-compose down && docker-compose up -d
after any.env
changes.
- Solution: Double-check your
- DNS Issues: Your domain not pointing correctly to your server IP.
- Solution: Use
ping api.yourdomain.com
ordig api.yourdomain.com
to verify DNS resolution. Ensure yourA record
is correctly set with your DNS provider.
- Solution: Use
- SSL/Certbot Errors: Certificate generation fails.
- Solution: Ensure your domain’s DNS has propagated, Nginx is running and correctly configured for the domain on port 80, and there are no firewall issues blocking port 80. Check Certbot logs for specific error messages.
- Firewall Configuration: If you’re running a firewall (e.g.,
ufw
), ensure ports 80, 443, and 3000 (for Studio access if not proxied) are open.- Example (ufw):
sudo ufw allow 80/tcp
,sudo ufw allow 443/tcp
,sudo ufw allow 3000/tcp
,sudo ufw enable
.
- Example (ufw):
- Checking Docker Logs: Always your first stop for debugging.
docker-compose logs
(for all services)- `docker-compose logs
` (e.g., `docker-compose logs auth`) - `docker-compose logs -f
` (follow logs in real-time)
Conclusion ✨
Self-hosting Supabase is a powerful way to gain control, optimize costs, and meet specific compliance needs for your application. While it requires a bit more hands-on effort compared to the managed service, the benefits can be significant for the right use case.
By following this comprehensive guide, you’ve successfully installed Supabase with Docker Compose, secured your API endpoints with a custom domain and SSL, and learned the essential maintenance practices. You now have a robust, scalable, and fully customizable backend at your fingertips!
Happy coding! 🚀