How to Install Supabase with Docker on Debian 11

Supabase is an open-source Firebase alternative that provides you with all the tools needed to develop your applications. Supabase offers a PostgreSQL database, user authentication, storage and a real-time API and integrates with popular frameworks and tools such as Angular, Flutter, Next.js, React, Svelte and Vue.

There are two ways of using Supabase. The first option is to sign up for their cloud-hosted app, which offers more features. The second option is to self-host the code using Docker. There are a few caveats on self-hosting, though. You cannot create or manage projects. Moreover, the entire project is in the beta phase, and you should use the self-hosted version only for testing and local development. For all other purposes, you should use their cloud application.

In this tutorial, you will learn how to install Supabase on a Debian 11 based server and proxy it via the Nginx server.

Prerequisites

  • A server running Debian 11 with a minimum of 2GB RAM.

  • A non-root user with sudo privileges.

  • A Domain name (supabase.example.com) pointing to the server.

  • Everything is updated.

    $ sudo apt update && sudo apt upgrade
    
  • Few packages that your system needs.

    $ sudo apt install nano ufw software-properties-common dirmngr apt-transport-https gnupg2 ca-certificates lsb-release debian-archive-keyring -y
    

    Some of these packages may already be installed on your system.

Step 1 - Configure Firewall

The first step is to configure the firewall. Debian comes with ufw (Uncomplicated Firewall).

Check if the firewall is running.

$ sudo ufw status

You should get the following output.

Status: inactive

Allow SSH port so that the firewall doesn't break the current connection on enabling it.

$ sudo ufw allow OpenSSH

Allow HTTP and HTTPS ports as well.

$ sudo ufw allow 80/tcp
$ sudo ufw allow 443/tcp

Enable the Firewall

$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

Check the status of the firewall again.

$ sudo ufw status

You should see a similar output.

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
80/tcp (v6)                ALLOW       Anywhere (v6)
443/tcp (v6)               ALLOW       Anywhere (v6)

Step 2 - Install Git

Install Git.

$ sudo apt install git

Confirm the installation.

$ git --version
git version 2.30.2

Step 3 - Install Docker

To install the latest version of Docker, add Docker's official GPG key.

$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Install the official Docker repository.

$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Update Debian system repositories.

$ sudo apt update

Install the latest version of Docker.

$ sudo apt install docker-ce docker-ce-cli containerd.io

Verify that Docker is running.

$ sudo systemctl status docker
? docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2022-03-21 03:19:09 UTC; 9s ago
TriggeredBy: ? docker.socket
       Docs: https://docs.docker.com
   Main PID: 15816 (dockerd)
      Tasks: 7
     Memory: 27.9M
        CPU: 566ms
     CGroup: /system.slice/docker.service
             ??15816 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

By default, Docker requires root privileges. If you want to avoid using sudo every time you run the docker command, add your username to the docker group.

$ sudo usermod -aG docker $(whoami)

You need to log out of the server and back in as the same user to enable this change.

Step 4 - Install Docker Compose

Docker Compose's latest available version is 2.0.x, but for compatibility reasons, we will be installing the older and stable v1.29.2.

Run the following command to download the stable release of Docker Compose.

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

Apply executable permissions to the binary file.

$ sudo chmod +x /usr/local/bin/docker-compose

Download and install the Docker Compose's bash completion script.

$ sudo curl \
    -L https://raw.githubusercontent.com/docker/compose/1.29.2/contrib/completion/bash/docker-compose \
    -o /etc/bash_completion.d/docker-compose

Run the following command to apply the changes.

$ source ~/.bashrc

Step 5 - Download and Configure Supabase

Clone the Supabase Github repository.

$ git clone --depth 1 https://github.com/supabase/supabase.git

The --depth 1 parameter does a shallow clone of the repository, i.e., it only pulls the latest commits and not the entire history. This is done to improve the performance.

Switch to the docker directory.

$ cd supabase/docker

Create the environment file from the example file.

$ cp .env.example .env

Open the newly created file for editing.

$ nano .env

Change the value of the variable POSTGRES_PASSWORD with a strongly generated unique password.

$ POSTGRES_PASSWORD=<yourpostgresqlpwd>

Generate another unique password with more than 32 characters with no special characters. Replace the value of JWT_SECRET with this password.

JWT_SECRET=<your32pluscharacterspwd>

You can use an online password generator like Bitwarden or 1password to create the above passwords.

Open the Supabase website and enter your JWT_SECRET to generate an ANON_KEY. Paste your JWT_SECRET in the box specified, select ANON_KEY from the Preconfigured Payload dropdown menu and click on the Generate JWT button to generate the token.

Supabase ANON KEY Generator

Copy and paste this token as the value for ANON_KEY in the .env file.

Similarly, repeat the same steps for generating the SERVICE_KEY by switching the Preconfigured Payload and pressing the Generate JWT button.

Supabase SERVICE KEY Generator

Copy the generated token and paste it as the value for the SERVICE_KEY in the .env file.

Configure the email SMTP settings by configuring the following variables. We are using Amazon's SES service for our tutorial.

[email protected]
SMTP_HOST=email-smtp.us-west-2.amazonaws.com
SMTP_PORT=587
SMTP_USER=<your_amazon_ses_user>
SMTP_PASS=<your_amazon_ses_password>
SMTP_SENDER_NAME=SupabaseAdmin

Configure the site URL.

SITE_URL=https://supabase.example.com

Configure the public REST URL.

PUBLIC_REST_URL=https://supabase.example.com/rest/v1/

Save the file by pressing Ctrl + X and entering Y when prompted.

The authentication mails sent via SMTP will have broken links because of a bug prevalent in Supabase. To fix this issue, open the docker-compose.yml file.

$ nano docker-compose.yml

Add the variable API_EXTERNAL_URL right below the GOTRUE_SITE_URL variable to look like the following.

 GOTRUE_SITE_URL: ${SITE_URL}
 API_EXTERNAL_URL: ${SITE_URL}

Save the file by pressing Ctrl + X and entering Y when prompted. This file will get overwritten every time you upgrade your Supabase installation. Hopefully, the bug will be fixed next time. You will need to add this variable manually to ensure the email bug doesn't repeat.

Open the volumes/api/kong.yml file for editing.

$ nano volumes/api/kong.yml

Under the consumers section, replace the anon user's key with the ANON_KEY generated before.

consumers:
  - username: anon
    keyauth_credentials:
      - key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE

Also, replace the service_role's key with the SERVICE_KEY generated above.

- username: service_role
    keyauth_credentials:
      - key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q

Save the file by pressing Ctrl + X and entering Y when prompted.

Step 6 - Install Supabase

Run Supabase by using the following command. This process will take some time.

$ docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up -d

Check the status of the running containers.

$ docker ps
CONTAINER ID   IMAGE                            COMMAND                   CREATED              STATUS                        PORTS                                                                                                                             NAMES
d25393873731   supabase/storage-api:v0.10.0     "/bin/sh -c \"./stora…"   About a minute ago   Up About a minute             5000/tcp                                                                                                                          supabase-storage
e6df7dcdd45b   supabase/gotrue:v2.5.21          "gotrue"                  About a minute ago   Up About a minute                                                                                                                                               supabase-auth
b3a758592d10   supabase/postgres-meta:v0.29.0   "postgres-meta"           About a minute ago   Up About a minute             0.0.0.0:5555->8080/tcp, :::5555->8080/tcp                                                                                         supabase-meta
cdb18c248f79   supabase/realtime:v0.21.0        "bash -c './prod/rel…"    About a minute ago   Up About a minute                                                                                                                                               supabase-realtime
71417337efae   postgrest/postgrest:v9.0.0       "/bin/postgrest"          About a minute ago   Up About a minute             3000/tcp                                                                                                                          supabase-rest
2d51af16bd1f   kong:2.1                         "/docker-entrypoint.…"    2 minutes ago        Up About a minute             0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 8001/tcp, 0.0.0.0:8443->8443/tcp, :::8443->8443/tcp, 8444/tcp                          supabase-kong
d6490380e4e8   supabase/postgres:14.1.0         "docker-entrypoint.s…"    2 minutes ago        Up About a minute             0.0.0.0:5432->5432/tcp, :::5432->5432/tcp
                                              supabase-db
40a49d1482fa   supabase/studio:latest           "docker-entrypoint.s…"    2 minutes ago        Up About a minute             0.0.0.0:3000->3000/tcp, :::3000->3000/tcp                                                                                         supabase-studio
3cce50db9782   inbucket/inbucket:stable         "/start-inbucket.sh …"    2 minutes ago        Up About a minute (healthy)   0.0.0.0:1100->1100/tcp, :::1100->1100/tcp, 0.0.0.0:2500->2500/tcp, :::2500->2500/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp   supabase-mail

Step 7 - Install SSL

To install an SSL certificate using Let's Encrypt, we need to install the Certbot tool.

We will use the Snapd package installer for that. Since most Debian servers don't ship with it, install the Snapd installer.

$ sudo apt install snapd

Ensure that your version of Snapd is up to date.

$ sudo snap install core && sudo snap refresh core

Install Certbot.

$ sudo snap install --classic certbot

Use the following command to ensure that the Certbot command can be run by creating a symbolic link to the /usr/bin directory.

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Verify the installation.

$ certbot --version
certbot 1.25.0

Generate the SSL certificate.

$ sudo certbot certonly --standalone --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m [email protected] -d supabase.example.com

The above command will download a certificate to the /etc/letsencrypt/live/supabase.example.com directory on your server.

Generate a Diffie-Hellman group certificate.

$ sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

Create a challenge webroot directory for Let's Encrypt auto-renewal.

$ sudo mkdir -p /var/lib/letsencrypt

Create a Cron Job to renew the SSL. It will run every day to check the certificate and renew if needed. For that, first, create the file /etc/cron.daily/certbot-renew and open it for editing.

$ sudo nano /etc/cron.daily/certbot-renew

Paste the following code.

#!/bin/sh
certbot renew --cert-name supabase.example.com --webroot -w /var/lib/letsencrypt/ --post-hook "systemctl reload nginx"

Save the file by pressing Ctrl + X and entering Y when prompted.

Change the permissions on the task file to make it executable.

$ sudo chmod +x /etc/cron.daily/certbot-renew

Step 8 - Install and Configure Nginx

Debian ships with an older version of Nginx. You need to download the official Nginx repository to install the latest version.

Import the official Nginx signing key.

$ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
	 | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

Add the repository for Nginx's stable version.

$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] \
    http://nginx.org/packages/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

Update the Debian repositories.

$ sudo apt update

Install Nginx.

$ sudo apt install nginx

Verify the installation. Make sure you use sudo every time you run the Nginx command on Debian. Otherwise, it won't work.

$ sudo nginx -v
nginx version: nginx/1.20.2

Create and open the file /etc/nginx/conf.d/supabase.conf for editing.

$ sudo nano /etc/nginx/conf.d/supabase.conf

Paste the following code in it.

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}
upstream supabase {
  	server localhost:3000;
}
upstream kong {
  	server localhost:8000;
}
# enforce HTTPS
server {
    listen       80; 
    listen 		[::]:80;
    server_name  supabase.example.com;
    return 301   https://$host$request_uri;
}
server {
    listen       443 ssl http2;
    listen 		[::]:443 ssl http2;
    server_name  supabase.example.com;

    access_log  /var/log/nginx/supabase.access.log;
    error_log   /var/log/nginx/supabase.error.log;
    
    gzip on;
    
    # SSL
    ssl_certificate      /etc/letsencrypt/live/supabase.example.com/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/supabase.example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/supabase.example.com/chain.pem;
    ssl_session_timeout  5m;
    ssl_session_cache shared:MozSSL:10m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    resolver 8.8.8.8;
    
    client_max_body_size 100m;
    
    # REST API
	location ~ ^/rest/v1/(.*)$ {
    	proxy_set_header Host $host;
	    proxy_pass http://kong;
	    proxy_redirect off;
  	}

	# Authentication
	location ~ ^/auth/v1/(.*)$ {
    	proxy_set_header Host $host;
	    proxy_pass http://kong;
	    proxy_redirect off;
  	}

	# Realtime
	location ~ ^/realtime/v1/(.*)$ {
    	proxy_redirect off;
	    proxy_pass http://kong;
	    proxy_http_version 1.1;
	    proxy_set_header Upgrade $http_upgrade;
    	proxy_set_header Connection $connection_upgrade;
	    proxy_set_header Host $host;
	}
    
    # Studio
	location / {
    	proxy_set_header Host $host;
	    proxy_pass http://supabase;
	    proxy_redirect off;
	    proxy_set_header Upgrade $http_upgrade;
  	}
}

Save the file by pressing Ctrl + X and entering Y when prompted once finished.

Open the file /etc/nginx/nginx.conf for editing.

$ sudo nano /etc/nginx/nginx.conf

Add the following line before the line include /etc/nginx/conf.d/*.conf;.

server_names_hash_bucket_size  64;

Save the file by pressing Ctrl + X and entering Y when prompted.

Verify the Nginx configuration file syntax.

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Start the Nginx service to enable the new configuration.

$ sudo systemctl start nginx

Step 9 - Access Supabase

You can now access Supabase via the URL https://supabase.example.com in your web browser.

Supabase Studio

You can't create new projects, but you can manage the existing ones and configure the database, storage, and authentication.

Step 10 - Enable HTTP authentication

Supabase doesn't provide user management, so you need to secure your Supabase installation. The easiest way to do that is to enable HTTP authentication using the Nginx server.

Install apache2-utils package for Nginx.

$ sudo apt install apache2-utils

Create a password file for the user supabase. You can choose any username you want. It will prompt you for a new password. Enter a strong password to finish setting up the file.

$ sudo htpasswd -c /etc/nginx/.htpasswd supabase
New password:
Re-type new password:
Adding password for user supabase

The -c flag tells the utility to create a new file. If you omit the file, you need to specify the location of an existing file.

Open the /etc/nginx/conf.d/supabase.conf for editing.

$ sudo nano /etc/nginx/conf.d/supabase.conf

Make the changes in the following sections of the file, as shown below.

# REST API
	location ~ ^/rest/v1/(.*)$ {
	    auth_basic off;
    	proxy_set_header Host $host;
	    proxy_pass http://kong;
	    proxy_redirect off;
  	}

	# Authentication
	location ~ ^/auth/v1/(.*)$ {
	    auth_basic off;
    	proxy_set_header Host $host;
	    proxy_pass http://kong;
	    proxy_redirect off;
  	}

	# Realtime
	location ~ ^/realtime/v1/(.*)$ {
	    auth_basic off;
    	proxy_redirect off;
	    proxy_pass http://kong;
	    proxy_http_version 1.1;
	    proxy_set_header Upgrade $http_upgrade;
    	proxy_set_header Connection $connection_upgrade;
	    proxy_set_header Host $host;
	}
    
    # Studio
	location / {
	    auth_basic “Supabase Studio Login”;
	    auth_basic_user_file /etc/nginx/.htpasswd;
    
    	proxy_set_header Host $host;
	    proxy_pass http://supabase;
	    proxy_redirect off;
	    proxy_set_header Upgrade $http_upgrade;
  	}

Save the file by pressing Ctrl + X and entering Y when prompted.

Verify the configuration.

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Restart the Nginx service.

$ sudo systemctl restart nginx

The next time you open Supabase, you will be greeted with the following login screen.

Supabase Nginx HTTP Authentication

Step 11 - Update Supabase

Supabase is a constant work-in-progress product and will undergo constant changes. If you use it as a self-hosted instance, you must keep your Supabase installation updated.

Switch to the Supabase directory.

$ cd ~/supabase

Pull the latest Github repository.

$ git pull

Look for any changes in the docker/.env, docker/volumes/api/kong.yml files for anything that requires reconfiguration.

Shut down and clean up the existing Docker containers.

$ docker-compose down --remove-orphans

Pull the latest images.

$ docker-compose pull

Start the containers again.

$ docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up -d

Conclusion

This concludes our tutorial on installing Supabase on a Debian 11 based server using the Nginx server as reverse-proxy. If you have any questions, post them in the comments below.

Share this page:

Suggested articles

1 Comment(s)

Add comment

Comments

By: [email protected] at: 2022-05-04 16:33:29

Hi Singh, thanks you for the sharing, but i tried to host supabase on my 2x vps, both failed, it always shows connecting the default project.

could you help to check on it ?