ν™”. 8μ›” 12th, 2025

G: Are you a developer who loves the power of Supabase but wants more control, privacy, or perhaps just to save on cloud costs? Then you’ve landed on the right page! Supabase offers a fantastic open-source alternative to Firebase, providing a PostgreSQL database, authentication, real-time subscriptions, storage, and edge functions. While their hosted service is excellent, self-hosting gives you unparalleled flexibility and data sovereignty.

This comprehensive guide will walk you through the exciting journey of deploying Supabase on your own server using Docker, connecting it to a custom domain, and securing it with SSL. Get ready to unlock the full potential of your stack! πŸš€


Why Self-Host Supabase? πŸ€”

Before we dive into the technicalities, let’s briefly touch upon why you might choose to self-host:

  • Ultimate Control: You manage the underlying infrastructure, allowing for deep customization, specific optimizations, and fine-grained security policies.
  • Cost Efficiency: For projects with predictable or high resource usage, running on your own hardware or a bare-bones VPS can be significantly cheaper than a managed service.
  • Data Sovereignty & Privacy: Keep your data on your own servers, adhering to specific compliance requirements or simply for peace of mind.
  • Learning Opportunity: It’s an excellent way to deepen your understanding of Docker, Nginx, PostgreSQL, and general DevOps practices.
  • Customization: Need a specific PostgreSQL extension or a custom auth flow not directly supported by the managed service? You have the freedom to implement it.

Section 1: Prerequisites & Preparation πŸ› οΈ

Before we start, ensure you have the following in place:

1. A Linux Server (VPS or Dedicated) 🐧

  • Operating System: Ubuntu 20.04+ LTS or Debian 11+ is recommended.
  • Resources:
    • Minimum: 2 CPU Cores, 4 GB RAM, 50 GB SSD storage. (This is for a basic setup. For production, scale up!).
    • Recommended: 4 CPU Cores, 8 GB RAM, 100+ GB SSD storage for better performance and future growth.
  • Provider: DigitalOcean, Linode, AWS EC2, Google Cloud, Vultr, or your own on-premise server.

2. Domain Name 🌐

  • You’ll need a registered domain name (e.g., yourproject.com).
  • We’ll be setting up subdomains like api.yourproject.com, auth.yourproject.com, storage.yourproject.com.

3. Essential Software Installed on Your Server πŸ“¦

Make sure your server is up-to-date and has these tools:

  • SSH Access: Ensure you can SSH into your server.
  • Git: For cloning the Supabase repository.
    sudo apt update
    sudo apt install git -y
  • Docker: The containerization platform.

    # Install Docker (if not already installed)
    sudo apt update
    sudo apt install ca-certificates curl gnupg lsb-release -y
    sudo mkdir -p /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt update
    sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
    
    # Add your user to the docker group to run docker commands without sudo
    sudo usermod -aG docker $USER
    newgrp docker # Apply group changes immediately
    
    # Verify installation
    docker run hello-world
  • Docker Compose (as a plugin for Docker): Docker Compose is now typically installed as a plugin with docker-compose-plugin. Verify by running docker compose version (note the space, not a hyphen).

Section 2: Setting Up Supabase with Docker Compose ✨

Supabase provides an excellent docker-compose setup that makes deploying its core services incredibly straightforward.

1. Clone the Supabase Repository πŸ’Ύ

First, connect to your server via SSH. Then, clone the official Supabase repository. We’ll use --depth 1 to get only the latest commit, saving time and space.

cd ~
git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker

2. Configure Your Environment Variables (The .env File) πŸ”’

The docker directory contains an .env.example file. This file holds crucial configuration for your Supabase services. We need to copy it and then customize it.

cp .env.example .env
nano .env # or use vim, your preferred text editor

Inside the .env file, you’ll see many variables. Here are the most critical ones you must change:

  • JWT_SECRET: This is the secret key used to sign and verify JSON Web Tokens. Generate a strong, random 32-character string for this (e.g., using openssl rand -base64 32). NEVER use the default!
  • ANON_KEY: Your public API key. This key is safe to embed in your client-side code. Generate a random UUID or a long random string.
  • SERVICE_ROLE_KEY: Your private, administrative API key. This key has full bypass privileges on your Row Level Security (RLS) policies. Keep this absolutely secret and use it only on your backend/server. Generate a random UUID or a long random string.
  • POSTGRES_PASSWORD: The password for your PostgreSQL database’s postgres user. Use a strong, unique password.
  • POSTGRES_DB: (Optional) The name of your database. postgres is the default.
  • AUTH_MAILER_URL: If you plan to use email authentication, configure an SMTP server URL here. Example: smtps://user:password@smtp.example.com:465. If not, leave it commented or blank for now.
  • AUTH_EXTERNAL_URL: This is the public URL for your Supabase Auth service. We’ll set this to https://auth.yourproject.com later. For now, you can leave it as http://localhost:8000 or the server IP if you want to test locally.
  • STORAGE_URL: Public URL for your Storage service. Set to https://storage.yourproject.com.
  • API_URL: Public URL for your PostgREST (API) service. Set to https://api.yourproject.com.
  • DASHBOARD_URL: If you want to access the Supabase Studio dashboard from a specific domain, set this (e.g., https://dashboard.yourproject.com). Otherwise, you’ll access it directly via the server IP and port (e.g., http://YOUR_SERVER_IP:8000).

Example .env snippet (after modification):

# General
JWT_SECRET="YOUR_SUPER_STRONG_RANDOM_32_CHAR_JWT_SECRET"
ANON_KEY="your_anon_public_key_here_e.g._random_uuid_or_string"
SERVICE_ROLE_KEY="your_super_secret_service_role_key_here"

# Database
POSTGRES_PASSWORD="YourSuperSecureDbPassword"
POSTGRES_DB="supabase" # Or your preferred database name

# API URLs (IMPORTANT for domain setup later)
AUTH_EXTERNAL_URL="https://auth.yourproject.com"
STORAGE_URL="https://storage.yourproject.com"
API_URL="https://api.yourproject.com"
# DASHBOARD_URL="https://dashboard.yourproject.com" # Uncomment if you want a dedicated dashboard domain

# Mailer (for Auth emails) - Uncomment and configure if needed
# AUTH_MAILER_URL="smtps://user:password@smtp.example.com:465"
# ... many other mailer options ...

# Analytics (optional, for internal Supabase metrics)
# ANALYTICS_ENABLED="false"

# ... (other variables for GoTrue, PostgREST, Storage, Realtime, etc. - usually fine as default)

Save and exit the file.

3. Bring Up Supabase Services with Docker Compose πŸš€

Now, from within the supabase/docker directory, run the following command:

docker compose up -d
  • docker compose up: Starts all services defined in docker-compose.yml.
  • -d: Runs the containers in “detached” mode (in the background).

This command will:

  1. Pull all necessary Docker images (PostgreSQL, PostgREST, GoTrue, Storage, Realtime, InfluxDB, etc.). This might take a few minutes depending on your internet connection.
  2. Create and start the containers.
  3. Set up the necessary networks and volumes.

You can check the status of your containers with:

docker ps

You should see a list of Supabase-related containers running, including supabase_db, supabase_rest, supabase_auth, supabase_storage, supabase_realtime, and others. If any container is not running, check `docker logs

` for errors. — ### Section 3: Configuring Your Domain & SSL πŸŒπŸ”’ This is where your Supabase instance becomes publicly accessible and secure. We’ll use Nginx as a reverse proxy and Certbot (Let’s Encrypt) for free SSL certificates. #### 1. DNS Configuration πŸ’‘ Go to your domain registrar or DNS provider (e.g., Cloudflare, GoDaddy, Namecheap) and add the following **A records** pointing to your server’s public IP address: * `A` record: `api.yourproject.com` -> `YOUR_SERVER_IP` * `A` record: `auth.yourproject.com` -> `YOUR_SERVER_IP` * `A` record: `storage.yourproject.com` -> `YOUR_SERVER_IP` * (Optional) `A` record: `dashboard.yourproject.com` -> `YOUR_SERVER_IP` (If you want a dedicated domain for Studio) **Example:** | Type | Name | Value | TTL (sec) | | :— | :———- | :———— | :——– | | `A` | `api` | `123.45.67.89` | `3600` | | `A` | `auth` | `123.45.67.89` | `3600` | | `A` | `storage` | `123.45.67.89` | `3600` | | `A` | `dashboard` | `123.45.67.89` | `3600` | *Allow some time for DNS changes to propagate globally (can take minutes to a few hours).* You can check propagation using `https://dnschecker.org/`. #### 2. Install and Configure Nginx (Reverse Proxy) 🚦 Nginx will sit in front of your Dockerized Supabase services. It will handle incoming web requests, direct them to the correct Supabase container, and manage SSL termination. “`bash sudo apt update sudo apt install nginx -y “` Now, let’s create an Nginx configuration file for Supabase. “`bash sudo nano /etc/nginx/sites-available/supabase.conf “` Paste the following configuration. **Remember to replace `yourproject.com` with your actual domain!** “`nginx server { listen 80; server_name api.yourproject.com auth.yourproject.com storage.yourproject.com dashboard.yourproject.com; # Add your domains here # Redirect all HTTP to HTTPS return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name api.yourproject.com; # Your API domain ssl_certificate /etc/letsencrypt/live/api.yourproject.com/fullchain.pem; # Managed by Certbot ssl_certificate_key /etc/letsencrypt/live/api.yourproject.com/privkey.pem; # Managed by Certbot # Standard Nginx proxy settings for Supabase proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_pass_request_headers on; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection “Upgrade”; location / { proxy_pass http://localhost:8000; # Supabase API (PostgREST) runs on port 8000 } } server { listen 443 ssl http2; server_name auth.yourproject.com; # Your Auth domain ssl_certificate /etc/letsencrypt/live/auth.yourproject.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/auth.yourproject.com/privkey.pem; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_pass_request_headers on; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection “Upgrade”; location / { proxy_pass http://localhost:9000; # Supabase Auth (GoTrue) runs on port 9000 } } server { listen 443 ssl http2; server_name storage.yourproject.com; # Your Storage domain ssl_certificate /etc/letsencrypt/live/storage.yourproject.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/storage.yourproject.com/privkey.pem; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_pass_request_headers on; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection “Upgrade”; location / { proxy_pass http://localhost:9001; # Supabase Storage runs on port 9001 } } # Optional: Supabase Studio Dashboard # If you configured DASHBOARD_URL in .env, add this server { listen 443 ssl http2; server_name dashboard.yourproject.com; # Your Dashboard domain ssl_certificate /etc/letsencrypt/live/dashboard.yourproject.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/dashboard.yourproject.com/privkey.pem; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_pass_request_headers on; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection “Upgrade”; location / { proxy_pass http://localhost:8000; # Studio dashboard is served by PostgREST/API container on port 8000 by default. # Ensure you’ve enabled the dashboard service if you’re trying to access it from a different subdomain. # In a typical Supabase Docker setup, PostgREST handles the dashboard assets. } } # Add more server blocks if you decide to expose other services (e.g., Realtime on port 8002) # For Realtime (WebSockets): # server { # listen 443 ssl http2; # server_name realtime.yourproject.com; # # ssl_certificate /etc/letsencrypt/live/realtime.yourproject.com/fullchain.pem; # ssl_certificate_key /etc/letsencrypt/live/realtime.yourproject.com/privkey.pem; # # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # proxy_set_header X-Real-IP $remote_addr; # proxy_set_header Host $host; # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection “Upgrade”; # # location / { # proxy_pass http://localhost:8002; # Supabase Realtime runs on port 8002 # } # } “` Save and exit the file. Now, enable the Nginx configuration by creating a symbolic link: “`bash sudo ln -s /etc/nginx/sites-available/supabase.conf /etc/nginx/sites-enabled/ “` Test your Nginx configuration for syntax errors: “`bash sudo nginx -t “` You should see: `nginx: configuration file /etc/nginx/nginx.conf syntax is ok` and `nginx: configuration file /etc/nginx/nginx.conf test is successful`. If there are errors, correct them before proceeding. Finally, restart Nginx to apply the changes: “`bash sudo systemctl restart nginx “` #### 3. Install and Configure Certbot (Let’s Encrypt SSL) πŸ›‘οΈ Certbot automates the process of obtaining and renewing free SSL certificates from Let’s Encrypt. “`bash # Install Certbot via snap (recommended) sudo snap install core sudo snap refresh core sudo snap install –classic certbot sudo ln -s /snap/bin/certbot /usr/bin/certbot “` Now, run Certbot to obtain your SSL certificates. It will automatically detect your Nginx configuration and set up SSL. “`bash sudo certbot –nginx -d api.yourproject.com -d auth.yourproject.com -d storage.yourproject.com -d dashboard.yourproject.com # Add all your domains here! “` Follow the prompts: * Enter your email address for urgent renewal or security notices. * Agree to the Terms of Service. * Decide if you want to share your email with EFF (Electronic Frontier Foundation). * Certbot will ask if you want to redirect HTTP traffic to HTTPS. **Choose option 2 (Redirect)**. If successful, you’ll see a congratulatory message! πŸŽ‰ **Verify Auto-Renewal:** Let’s Encrypt certificates are valid for 90 days. Certbot automatically sets up a cron job for renewal. You can test the renewal process with a dry run: “`bash sudo certbot renew –dry-run “` If this completes without errors, your certificates will auto-renew. — ### Section 4: Post-Installation & Verification βœ… Now that everything is set up, let’s verify your self-hosted Supabase instance. #### 1. Access Supabase Studio πŸ–₯️ Open your web browser and navigate to `https://dashboard.yourproject.com` (or `https://api.yourproject.com` if you didn’t set a dedicated dashboard subdomain, as the API typically serves the studio assets). You should see the Supabase Studio login page. The default user is `supabase` and the password is the `POSTGRES_PASSWORD` you set in your `.env` file. Once logged in, you can see your projects, manage tables, set up RLS, and more, just like the hosted version! #### 2. Test API Endpoints πŸš€ Let’s test if your Supabase API is reachable and correctly configured. You can use `curl` from your local machine or a tool like Postman. **Example: Fetching projects (adjust if you have RLS enabled):** “`bash curl -v -H “apikey: YOUR_ANON_KEY_FROM_DOTENV” \ -H “Authorization: Bearer YOUR_ANON_KEY_FROM_DOTENV” \ “https://api.yourproject.com/rest/v1/projects” “` Replace `YOUR_ANON_KEY_FROM_DOTENV` with the `ANON_KEY` you configured in your `.env` file and `yourproject.com` with your domain. You should receive a JSON response, likely an empty array `[]` if you haven’t created any tables yet. **Example: Testing Auth Signup:** You can also try using the Supabase JavaScript client in a simple HTML file or Node.js script: “`javascript import { createClient } from ‘@supabase/supabase-js’ const supabaseUrl = ‘https://api.yourproject.com’ // Your API URL const supabaseAnonKey = ‘YOUR_ANON_KEY_FROM_DOTENV’ // Your ANON_KEY const supabase = createClient(supabaseUrl, supabaseAnonKey) async function signUpUser() { const { data, error } = await supabase.auth.signUp({ email: ‘test@example.com’, password: ‘secure_password’, }); if (error) { console.error(‘Signup error:’, error); } else { console.log(‘User signed up:’, data); // You’ll likely need email confirmation if AUTH_MAILER_URL is set } } signUpUser(); “` Make sure to replace the placeholders! #### 3. Essential Security Measures πŸ”’ * **Firewall (UFW):** Restrict access to your server’s ports. “`bash sudo ufw enable # Enable the firewall (be careful, might disconnect SSH!) sudo ufw allow ssh # Allow SSH access sudo ufw allow http # Allow HTTP (port 80) sudo ufw allow https # Allow HTTPS (port 443) # If you need to open other ports (e.g., Postgres directly for a specific reason) # sudo ufw allow 5432/tcp sudo ufw status verbose “` * **Strong Passwords & Secrets:** Ensure `JWT_SECRET`, `ANON_KEY`, `SERVICE_ROLE_KEY`, and `POSTGRES_PASSWORD` are all very strong and unique. Never commit your `.env` file to public repositories! * **Regular Updates:** Keep your OS, Docker, and Supabase containers updated. * **Backups:** Set up regular database backups (see next section). — ### Section 5: Advanced Tips & Troubleshooting πŸ’‘ #### 1. Updating Your Supabase Instance πŸ”„ When new versions of Supabase components are released, you’ll want to update your self-hosted instance. “`bash cd ~/supabase/docker git pull origin master # Pull the latest changes from the Supabase repo docker compose pull # Pull the latest Docker images for all services docker compose up -d –remove-orphans # Recreate containers with new images, removing old ones “` Always check the official Supabase release notes or self-hosting guides for any breaking changes or specific migration steps when performing major updates. #### 2. Database Backups πŸ’Ύ This is CRITICAL. Your data is your most valuable asset. * **Manual Backup (for PostgreSQL):** “`bash docker exec supabase_db pg_dump -U postgres -d supabase > supabase_backup_$(date +%Y%m%d%H%M%S).sql “` This will create a SQL dump of your `supabase` database (or whatever you named it in `.env`) inside the `supabase/docker` directory. * **Automate Backups:** Use a cron job to run the `pg_dump` command regularly and ideally push the backups to an object storage service (AWS S3, DigitalOcean Spaces, etc.) for off-site redundancy. #### 3. Monitoring πŸ“Š * **Docker Stats:** `docker stats` gives you real-time resource usage of your containers. * **Docker Logs:** `docker logs ` (e.g., `docker logs supabase_auth`) is essential for debugging issues. * **Server Monitoring:** Tools like `htop`, `nmon`, or dedicated monitoring solutions (Prometheus, Grafana, Datadog) can help you keep an eye on server health. #### 4. Common Troubleshooting 🀯 * **Containers Not Running:** * `docker ps -a`: See all containers, even stopped ones. * `docker logs `: Check the logs for specific error messages. * Check for port conflicts (e.g., if another service is using 80, 443, 8000, 9000, 9001). * **DNS Issues:** * `dig api.yourproject.com` (or `nslookup`) from your local machine to ensure your DNS records are pointing to the correct IP. * Check DNS propagation on `https://dnschecker.org/`. * **Nginx Errors:** * `sudo nginx -t`: Test Nginx config syntax. * `sudo systemctl status nginx`: Check Nginx service status. * `sudo journalctl -xe | grep nginx`: Look for Nginx errors in system logs. * **SSL Certificate Issues:** * `sudo certbot certificates`: List your certificates and their expiry dates. * Ensure Nginx is correctly pointing to the `.pem` files in `/etc/letsencrypt/live/`. * **`.env` File Mismatches:** Double-check that `AUTH_EXTERNAL_URL`, `STORAGE_URL`, and `API_URL` in your `.env` exactly match your domain setup and that `JWT_SECRET`, `ANON_KEY`, and `SERVICE_ROLE_KEY` are correctly generated and unique. — ### Conclusion: Your Supabase Powerhouse Awaits! πŸ₯³ Congratulations! You’ve successfully deployed your very own Supabase instance on your server, complete with Docker orchestration, a custom domain, and secure SSL. This setup gives you immense power and flexibility over your backend, allowing you to build amazing applications with full control over your data and infrastructure. While self-hosting requires a bit more hands-on effort, the benefits in terms of cost, customization, and learning are truly rewarding. Keep exploring, keep building, and enjoy your new Supabase powerhouse! πŸ§‘β€πŸ’» If you encounter any issues or have cool tips to share, feel free to drop them in the comments below! Happy coding!

λ‹΅κΈ€ 남기기

이메일 μ£Όμ†ŒλŠ” κ³΅κ°œλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ν•„μˆ˜ ν•„λ“œλŠ” *둜 ν‘œμ‹œλ©λ‹ˆλ‹€