화. 8월 12th, 2025

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
    • Docker Compose: A tool for defining and running multi-container Docker applications.
      • Installation Hint (Ubuntu): sudo apt-get install docker-compose -y
    • Git: To clone the Supabase repository.
      • Installation Hint (Ubuntu): sudo apt-get install git -y
  • 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.

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.

  1. Copy the example file:

    cp .env.example .env
  2. Edit the .env file: Open the newly created .env file with your preferred text editor (e.g., nano .env or vim .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"
    • 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"
    • 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"
    • DB_PASSWORD: The password for your Postgres database user (postgres). Choose a strong, random password.
      • Example: DB_PASSWORD="YourSuperSecureDBPassword123"
    • POSTGRES_PASSWORD: Alias for DB_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)
    • NEXT_PUBLIC_SUPABASE_URL: The URL your client applications will use to connect to Supabase. This should match your PUBLIC_URL.
    • NEXT_PUBLIC_SUPABASE_ANON_KEY: This should match your ANON_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 a running 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 your DB_PASSWORD from the .env file as the password.

    🎉 Congratulations! Your core Supabase instance is now up and running!


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

  1. 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
  2. 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 for kong (Supabase’s API Gateway), which routes requests to the appropriate backend services.
    • localhost:9999: Direct access to the auth service.
    • localhost:5000: Direct access to the storage service.
    • localhost:4000: Direct access to the realtime 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 port 8000 to the host machine’s port 8000. Nginx on the host then proxies to these exposed ports (localhost:8000, etc.).

  3. 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.

  1. Edit your .env file again:

    nano .env
  2. 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
  3. 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’s SUPABASE_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).
  • 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.

  1. Stop services: docker-compose down
  2. Pull latest changes: git pull origin main
  3. Rebuild/Update Docker images: docker-compose pull
  4. 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.

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`.
  • Incorrect Environment Variables: A misspelled JWT_SECRET or PUBLIC_URL can lead to authentication errors, broken magic links, or clients unable to connect.
    • Solution: Double-check your .env file carefully. Remember to docker-compose down && docker-compose up -d after any .env changes.
  • DNS Issues: Your domain not pointing correctly to your server IP.
    • Solution: Use ping api.yourdomain.com or dig api.yourdomain.com to verify DNS resolution. Ensure your A record is correctly set with your DNS provider.
  • 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.
  • 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! 🚀

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다