DEV Community

Arthur
Arthur

Posted on

20 1 1 1

Self Hosted Supabase with External Postgresql

Supabase is a self-hosted open source application back-end with a cloud offering (hosted by the developers of the platform). The platform is considered an open source firebase alternative by many. Whether or not that statement is true, is a discussion for another day.

What really stands out mostly for me, is the on the fly API & API documentation and most importantly simplified Row Level Security and realtime notifications are by far the biggest selling points.

Requirements

  • Linux (Debian)
  • Docker
  • Docker-compose
  • postgres
  • make
  • git

Dependencies

Install postgres and configure it to allow tcp connections from networks other than localhost.

You'll need to edit the postgres.conf and pg_hda.conf files.

Enter your psql terminal:

Get the postgres.conf file

SHOW config_file;
Enter fullscreen mode Exit fullscreen mode

You'll get the following output:

               config_file               
-----------------------------------------
 /etc/postgresql/12/main/postgresql.conf
(1 row)
Enter fullscreen mode Exit fullscreen mode

Open the file, then change the line:

 listen_addresses = 'localhost'
Enter fullscreen mode Exit fullscreen mode

To

 listen_addresses = '*'
Enter fullscreen mode Exit fullscreen mode

Get the pg_hba.conf file

SHOW hba_file;
Enter fullscreen mode Exit fullscreen mode

You'll get the following output:

              hba_file               
-------------------------------------
 /etc/postgresql/12/main/pg_hba.conf
(1 row)
Enter fullscreen mode Exit fullscreen mode

Open this file and add the following lines:

host    all             all              0.0.0.0/0                       md5
host    all             all              ::/0                            md5
Enter fullscreen mode Exit fullscreen mode

Then restart the postgres daemon, for the above changes to take, effect.
The default postgres user postgres does not have a password, now would be the right time to create a user with password, that will be used by supabase, and grant that user superuser with login, create db, create role, bypass rls roles

To install custom postgres extensions, the dev postgresl packages are required with make. To get these:


sudo apt install sudo apt install postgresql-server-dev-XX cmake make

Enter fullscreen mode Exit fullscreen mode

Where xx is the version of your postgresql db. In my case, the above command is:


sudo apt install sudo apt install postgresql-server-dev-12 cmake make

Enter fullscreen mode Exit fullscreen mode

Install docker and docker-compose. You'll also need to install Git.

Get the pg_jwt source code from github by cloning the project to your local machine:


git clone https://github.com/michelp/pgjwt

Enter fullscreen mode Exit fullscreen mode

Then change directory into the pgjwt directory, then:


sudo make install

Enter fullscreen mode Exit fullscreen mode

The above creates the pgjwt extension which will be installed with postgres create extension pgjwt

At this point we now need to get supabase from github; clone the repository t your local machine and cd into the project directory


git clone https://github.com/supabase/supabase && cd supabase

Enter fullscreen mode Exit fullscreen mode

Setting up secrets

Whilst you can use the defaults provided, it is advisable to set your own secrets.

cp .env.example .env
Enter fullscreen mode Exit fullscreen mode

I recommend you change the default anon key and service keys in the .env file

Use your JWT_SECRET to generate a anon and service API keys using the JWT Geneartor.

Replace the values in these files:

 .env:
    ANON_KEY - replace with an anon key
    SERVICE_ROLE_KEY - replace with a service key
 volumes/api/kong.yml
    anon - replace with an anon key
    service_role - replace with a service key
Enter fullscreen mode Exit fullscreen mode

Having made the above changes; in the .env file add credentials to your external postgresql database

POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password

POSTGRES_HOST=host.docker.internal
POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PORT=5432

The host.docker.internal on POSTGRES_HOST makes it possible for the container to connect to the host machine. Further the docker-compose.yaml has been changed ti include the line:

extra_hosts:
      - "host.docker.internal:host-gateway"
Enter fullscreen mode Exit fullscreen mode

at the end of each service to allow them to connect with the host. And the depends-on db removed everywhere in the file. The final file wil look as show below:

version: "3.8"
services:
studio:
container_name: supabase-studio
image: supabase/studio:latest
restart: unless-stopped
ports:
- ${STUDIO_PORT}:3000/tcp
environment:
STUDIO_PG_META_URL: http://meta:8080
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
SUPABASE_URL: http://kong:8000
SUPABASE_REST_URL: ${PUBLIC_REST_URL}
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
kong:
container_name: supabase-kong
image: kong:2.1
restart: unless-stopped
ports:
- ${KONG_HTTP_PORT}:8000/tcp
- ${KONG_HTTPS_PORT}:8443/tcp
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml
# https://github.com/supabase/cli/issues/14
KONG_DNS_ORDER: LAST,A,CNAME
KONG_PLUGINS: request-transformer,cors,key-auth,acl
volumes:
- ./volumes/api:/var/lib/kong
auth:
container_name: supabase-auth
image: supabase/gotrue:v2.5.21
restart: unless-stopped
environment:
GOTRUE_API_HOST: 0.0.0.0
GOTRUE_API_PORT: 9999
GOTRUE_DB_DRIVER: postgres
GOTRUE_DB_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres?search_path=auth
GOTRUE_SITE_URL: ${SITE_URL}
GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS}
GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP}
GOTRUE_JWT_ADMIN_ROLES: service_role
GOTRUE_JWT_AUD: authenticated
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
GOTRUE_JWT_EXP: ${JWT_EXPIRY}
GOTRUE_JWT_SECRET: ${JWT_SECRET}
GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP}
GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM}
GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL}
GOTRUE_SMTP_HOST: ${SMTP_HOST}
GOTRUE_SMTP_PORT: ${SMTP_PORT}
GOTRUE_SMTP_USER: ${SMTP_USER}
GOTRUE_SMTP_PASS: ${SMTP_PASS}
GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME}
GOTRUE_MAILER_URLPATHS_INVITE: /auth/v1/verify
GOTRUE_MAILER_URLPATHS_CONFIRMATION: /auth/v1/verify
GOTRUE_MAILER_URLPATHS_RECOVERY: /auth/v1/verify
GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: /auth/v1/verify
GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP}
GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM}
extra_hosts:
- "host.docker.internal:host-gateway"
rest:
container_name: supabase-rest
image: postgrest/postgrest:v9.0.0
restart: unless-stopped
environment:
PGRST_DB_URI: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres
PGRST_DB_SCHEMAS: public,storage, graphql_public
PGRST_DB_ANON_ROLE: anon
PGRST_JWT_SECRET: ${JWT_SECRET}
PGRST_DB_USE_LEGACY_GUCS: "false"
extra_hosts:
- "host.docker.internal:host-gateway"
realtime:
container_name: supabase-realtime
image: supabase/realtime:v0.22.4
restart: unless-stopped
environment:
DB_HOST: ${POSTGRES_HOST}
DB_PORT: 5432
DB_NAME: postgres
DB_USER: postgres
DB_PASSWORD: ${POSTGRES_PASSWORD}
DB_SSL: "false"
PORT: 4000
JWT_SECRET: ${JWT_SECRET}
REPLICATION_MODE: RLS
REPLICATION_POLL_INTERVAL: 100
SECURE_CHANNELS: "true"
SLOT_NAME: supabase_realtime_rls
TEMPORARY_SLOT: "true"
command: >
bash -c "./prod/rel/realtime/bin/realtime eval Realtime.Release.migrate
&& ./prod/rel/realtime/bin/realtime start"
extra_hosts:
- "host.docker.internal:host-gateway"
storage:
container_name: supabase-storage
image: supabase/storage-api:v0.10.0
depends_on:
- rest
restart: unless-stopped
environment:
ANON_KEY: ${ANON_KEY}
SERVICE_KEY: ${SERVICE_ROLE_KEY}
POSTGREST_URL: http://rest:3000
PGRST_JWT_SECRET: ${JWT_SECRET}
DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres
PGOPTIONS: -c search_path=storage,public
FILE_SIZE_LIMIT: 52428800
STORAGE_BACKEND: file
FILE_STORAGE_BACKEND_PATH: /var/lib/storage
TENANT_ID: stub
# TODO: https://github.com/supabase/storage-api/issues/55
REGION: stub
GLOBAL_S3_BUCKET: stub
volumes:
- ./volumes/storage:/var/lib/storage
extra_hosts:
- "host.docker.internal:host-gateway"
meta:
container_name: supabase-meta
image: supabase/postgres-meta:v0.29.0
restart: unless-stopped
environment:
PG_META_PORT: 8080
PG_META_DB_HOST: ${POSTGRES_HOST}
PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}
extra_hosts:
- "host.docker.internal:host-gateway"

Migrations

You are now ready to run database migrations. Migrations are essentially table schemas that supabase will need to initialize and set up everything it needs. These also include postgres extensions; Whilst supabase self hosted, now supports graphql, I will not include its configurations has it only works with postgres 14, which I have not yet tried.
While in the same supabase project, navigate to:

cd docker/volumes/db/init
Enter fullscreen mode Exit fullscreen mode

In that directory you'll find files that need to be run in their order.

And you should be good to go.

Retry later

Top comments (4)

Collapse
 
antonofthewoods profile image
Anton Melser

I had a look at the supabase/postgres image and there seems to be a LOT of bespoke stuff going on. I wonder how easy it is to try and upgrade to secure versions of postgres? 15.1 seems to have a few reasonably annoying bugs and security issues, and having at least 15.6 would be ideal...

Collapse
 
d3nai_labs_d2f079cfe45a3b profile image
D3nai Labs

hello at what time those information will be create on externam db

`CREATE ROLE authenticator LOGIN NOINHERIT NOCREATEDB NOCREATEROLE NOSUPERUSER;
CREATE ROLE anon NOLOGIN;
-- CREATE ROLE anonymous NOLOGIN;
CREATE ROLE webuser NOLOGIN;

ALTER USER authenticator WITH PASSWORD '{{ vault_supabase_db_password }}';

--ALTER USER pgbouncer WITH PASSWORD '{{ vault_supabase_db_password }}';
--ALTER USER supabase_auth_admin WITH PASSWORD '{{ vault_supabase_db_password }}';
--ALTER USER supabase_functions_admin WITH PASSWORD '{{ vault_supabase_db_password }}';
--ALTER USER supabase_storage_admin WITH PASSWORD '{{ vault_supabase_db_password }}';`

Collapse
 
akaghzi profile image
Asim Kaghzi

docker/volumes/db/init has data.sql which is zero bytes, any ideas?

Collapse
 
lmish profile image
lmish

Thank you man! So helpful.

Retry later