Self Hosted Supabase Configuration

Configure the bundled self-hosted Supabase that an sfp server provisions — the API gateway, GitHub login, keys, and day-to-day maintenance.

Overview

sfp server init provisions a self-hosted Supabase stack by default (--supabase-mode self-hosted). The stack runs inside the tenant's Docker Compose project alongside the sfp server, so there is no separate Supabase to install or clone: init writes the configuration, keys, and credentials into the tenant .env and the compose file.

The stack's API gateway is Envoy (compose service supabase-envoy). Envoy replaces the Kong gateway this stack previously used; the route and auth semantics are unchanged. The gateway keeps the network alias supabase-kong, and that hostname is what the tenant .env uses for SUPABASE_URL, AUTH_SUPABASE_URL, and SFP_OBJECT_STORAGE_ENDPOINT — it is deliberately left as-is. The alias exists so the persisted .env files of existing tenants keep working without an edit.

The gateway's configuration is vendor-managed: sfp server init and every sfp server update write the files under the tenant's config/envoy/ (envoy.yaml, cds.yaml, lds.template.yaml, envoy-entrypoint.sh) and remove the legacy Kong files (kong.yml, kong-entrypoint.sh). Do not edit these by hand; a release may replace them.

Self-hosting Supabase means you operate the database and auth yourself. Use Supabase Cloud (Teams/Pro) if you do not have in-house capacity to manage it.

An sfp server from v3 ships its own Supabase configuration in the default compose file. Only the options below (GitHub login, key rotation, maintenance) require operator action.

Routing from Caddy through the Envoy gateway to GoTrue, PostgREST, and storage
The bundled gateway: Envoy (service supabase-envoy) answers on the supabase-kong alias that the tenant .env uses.

What You'll Need

  • A host running Docker and Docker Compose (or Podman), with a domain name configured for the sfp server (e.g. sfp.example.com). DNS must resolve the domain to the host's public IP.
  • An outbound HTTPS path for certificate issuance (see TLS setup in Setting up sfp server).
  • For GitHub login: a GitHub OAuth App (see below) or a GitHub App for repository work.

The listed ports do not need to be reachable from the internet: Caddy terminates TLS and forwards only /auth/v1/* to the gateway. Supabase Studio is served on a dedicated, IP-restricted port. See Maintenance for the port list.

Quick Start

[1] Initialize the server

Run init in self-hosted mode. This is the default, so no flag is required, but the mode is explicit here:

sfp server init \
  --tenant <tenant> \
  --base-dir /opt/sfp-server \
  --mode prod \
  --domain sfp.example.com \
  --supabase-mode self-hosted \
  --config-file ./server.json

Init generates a Postgres password, a JWT secret, the Supabase anon and service role keys, and the Postgres Meta encryption key, and writes them to the tenant's .env. The internal URL is set to the gateway alias:

# <base-dir>/tenants/<tenant>/.env
SUPABASE_URL=http://supabase-kong:8000
AUTH_SUPABASE_URL=http://supabase-kong:8000

supabase-kong is the network alias of the supabase-envoy service. Leave both URLs untouched — they are internal Docker-network addresses and must not be changed to the public domain.

[2] Rotate keys (optional)

To generate fresh anon and service role keys signed by the tenant's JWT_SECRET, use the script in the "Generating API Keys" section of resources/docker/.env.supabase.template, or a JWT encoder with the claims:

{ "iss": "supabase", "ref": "your-project", "role": "anon", "iat": 1641769200 }
{ "iss": "supabase", "ref": "your-project", "role": "service_role", "iat": 1641769200 }

Replace SUPABASE_ANON_KEY and SUPABASE_SERVICE_KEY in the tenant .env with the new values. The gateway validates requests against these two keys; the container reads them at start, so restart the stack after rotating.

[3] Start the stack

sfp server start --tenant <tenant> --base-dir /opt/sfp-server

The Supabase services (database, auth, REST, storage, gateway, Meta) start with their own profile. Verify they are healthy:

cd /opt/sfp-server/tenants/<tenant>
docker compose ps
docker compose ps showing the self-hosted Supabase services running and healthy
The self-hosted Supabase services after sfp server start.

[4] Enable GitHub login

Create a GitHub OAuth App at GitHub Settings → Developer settings → OAuth Apps → New OAuth App:

FieldValue
Application name<your-company> sfp
Homepage URLhttps://sfp.example.com
Authorization callback URLhttps://sfp.example.com/auth/v1/callback

Then set these values in the tenant .env:

GITHUB_OAUTH_ENABLED=true
GITHUB_OAUTH_CLIENT_ID=<your-github-client-id>
GITHUB_OAUTH_CLIENT_SECRET=<your-github-client-secret>

Restart the stack for GoTrue to pick them up:

sfp server restart --tenant <tenant> --base-dir /opt/sfp-server

[5] Access Supabase Studio

Studio runs on a dedicated port (STUDIO_PORT, default 3100), reachable only from the IP ranges in ALLOWED_IPS. Open:

https://sfp.example.com:3100

Sign in with the admin credentials printed by sfp server init (or stored in credentials.json). The /studio path on the main domain redirects here.

Verify Everything Works

A running stack answers on the public domain through Caddy:

curl -sS https://sfp.example.com/auth/v1/health

A 200 OK from GoTrue confirms the gateway and auth service are up. The REST endpoint is not reachable from a browser or curl localhost:8000: the gateway exposes port 8000 only on the internal Docker network, and Caddy deliberately returns 404 for /rest/* and /storage/* on the public host. Clients must use the sfp server API and /auth/v1/* routes.

curl of the auth health endpoint returning 200 OK
Health check against the public domain through Caddy and the gateway.

Maintenance

docker compose logs -f supabase-envoy showing gateway access lines routed to the rest and auth clusters
Gateway access logs. The trailing field is the upstream cluster the request was routed to — rest, auth or storage.

Start / Stop / Restart

Use the sfp server lifecycle commands — they set up the compose project, the credentials, and the container engine for you:

sfp server start   --tenant <tenant> --base-dir /opt/sfp-server
sfp server stop    --tenant <tenant> --base-dir /opt/sfp-server
sfp server restart --tenant <tenant> --base-dir /opt/sfp-server

Update

sfp server update bumps the image, drains workflows, and applies migrations behind a Caddy maintenance page. It also reconciles the gateway configuration (config/envoy/) to the installed release and, after a postgres base-image bump, rebuilds collation-version indexes before migrations. See Updating sfp server.

View logs

From the tenant directory, tail individual services by their compose service name. The gateway service is supabase-envoy; the auth service is supabase-auth:

cd /opt/sfp-server/tenants/<tenant>
docker compose logs -f supabase-auth     # Auth (GoTrue) logs
docker compose logs -f supabase-envoy    # API gateway logs
docker compose logs -f supabase-envoy showing gateway access log lines
Gateway access logs from the supabase-envoy service.

Troubleshooting

  • /auth/v1/health does not answer. Check the database first (supabase-db), then the auth service and the gateway:
    cd /opt/sfp-server/tenants/<tenant>
    docker compose logs -f supabase-auth
    docker compose logs -f supabase-envoy
  • GitHub login fails or redirects back to an error. Confirm the OAuth App callback URL matches https://sfp.example.com/auth/v1/callback exactly, that GITHUB_OAUTH_ENABLED=true is set, and that the auth service restarted:
    docker compose logs -f supabase-auth
  • docker compose reports no Supabase services. Confirm the .env sets COMPOSE_PROFILES=supabase (the generated .env does by default) and that the container engine is the one the server uses (Docker vs Podman).
  • Logins are interrupted every release. A postgres base-image bump can change the glibc collation version and force a one-time index rebuild on the next update. It extends that update's maintenance window once per database; see Updating sfp server → Rebuild indexes after a postgres image bump.
docker compose logs -f supabase-auth showing GoTrue startup and request logs
Auth service logs for diagnosing GitHub login.

Security Checklist

Before going live:

  • Admin credentials retrieved from credentials.json and stored securely.
  • A strong POSTGRES_PASSWORD and JWT_SECRET (32+ characters) are in the tenant .env.
  • ALLOWED_IPS is set to the ranges that may reach Studio (and the Hatchet dashboard and npm registry ports).
  • The gateway configuration under config/envoy/ is the release-managed copy — do not hand-edit it.
  • Volume-level backups cover the databases (see Backup and restore).

Need Help?

On this page