Skip to content

Documentation

Self-hosting

Run the same software we host, on a server you control. It is three containers: Postgres 17, the web app and the gateway. No third-party service is called.

What runs

PartRolePort
Postgres 17Holds the control database (accounts, projects, plans) and one database per project. Needs a superuser and logical WAL (for realtime).5432
webThe dashboard, this website and the control API. Applies its own migrations when it starts and creates the administrator account.3000
gatewayThe data plane: REST, auth, storage and realtime for every project. Also runs the background worker: backups, restores, imports, cron jobs and webhooks.3195

Requirements

  • A Linux server with Docker and Docker Compose. 2 GB of RAM is enough to try it; size up with the number of projects.
  • Two DNS names, for example vulcbase.example.com (dashboard) and api.example.com (project APIs), and a reverse proxy that terminates TLS in front of ports 3000 and 3195.
  • Optionally a wildcard record *.api.example.com and certificate, so each project gets its own subdomain.
  • An SMTP account to send auth and invite emails.

Docker Compose

Get the source, copy the example environment file, fill it in, and start the stack.

cp .env.example .env     # edit it: see the variables belowdocker compose -f docker-compose.prod.yml up -d --build

The Compose file looks like this (shortened):

docker-compose.prod.yml
services:  pg:    image: pgvector/pgvector:pg17    restart: unless-stopped    command: ["postgres", "-c", "max_connections=500", "-c", "wal_level=logical"]    environment:      POSTGRES_USER: postgres      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}      POSTGRES_DB: vulcbase    volumes:      - vulcbase-pg:/var/lib/postgresql/data   gateway:    build: { context: ., dockerfile: apps/gateway/Dockerfile }    restart: unless-stopped    depends_on: [pg]    env_file: .env    environment:      DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@pg:5432/vulcbase      CLUSTER_ADMIN_URL: postgresql://postgres:${POSTGRES_PASSWORD}@pg:5432/postgres      DATA_DIR: /data    ports: ["3195:3195"]    volumes: [vulcbase-data:/data]   web:    build: { context: ., dockerfile: apps/web/Dockerfile }    restart: unless-stopped    depends_on: [pg]    env_file: .env    environment:      DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@pg:5432/vulcbase      CLUSTER_ADMIN_URL: postgresql://postgres:${POSTGRES_PASSWORD}@pg:5432/postgres      GATEWAY_INTERNAL_URL: http://gateway:3195      DATA_DIR: /data    ports: ["3000:3000"]    volumes: [vulcbase-data:/data] volumes:  vulcbase-pg:  vulcbase-data:

When the web container starts it applies its migrations and creates the administrator account from ADMIN_EMAIL and ADMIN_PASSWORD. Sign in with it, then create organizations and projects as usual. Health checks: /api/health on the web app and /health on the gateway.

Environment variables

VariableWhat it is
DATABASE_URLConnection string of the control-plane database. Set by the Compose file.
CLUSTER_ADMIN_URLSuperuser connection to the cluster that holds project databases. Used to create and drop them. Set by the Compose file.
APP_SECRETLong random string that encrypts project secrets at rest. Never change it once projects exist.
APP_URLPublic address of the dashboard.
GATEWAY_URLPublic address of the gateway. Projects are served at GATEWAY_URL/p/<ref> unless a wildcard domain is set.
API_WILDCARD_DOMAINOptional. When set, each project is served at https://<ref>.<domain>.
PUBLIC_DB_HOSTOptional. The host:port shown in connection strings, when you expose Postgres for psql and ORMs.
ADMIN_EMAIL, ADMIN_PASSWORDThe first administrator, created at startup.
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROMOutgoing mail for account and auth emails.
DATA_DIRWhere backups, imports and storage files are kept. Mount a volume here. Set by the Compose file.
PG_BIN_DIRFolder with pg_dump, pg_restore and psql (version 17). Empty uses the PATH.
.env
POSTGRES_PASSWORD=<a long random password>APP_SECRET=<64 random characters>APP_URL=https://vulcbase.example.comGATEWAY_URL=https://api.example.comAPI_WILDCARD_DOMAIN=            # optional, see DNS belowPUBLIC_DB_HOST=db.example.com:5432ADMIN_EMAIL=you@example.comADMIN_PASSWORD=<a long password>SMTP_HOST=smtp.example.comSMTP_PORT=587SMTP_USER=<user>SMTP_PASSWORD=<password>SMTP_FROM="Vulcbase <no-reply@example.com>"

Back up APP_SECRET

Without it, encrypted project secrets cannot be read. Store it with your other secrets.

DNS and reverse proxy

Point the dashboard name at port 3000 and the API name at port 3195. Without a wildcard record, projects use https://api.example.com/p/<ref>, which works with supabase-js just the same. With API_WILDCARD_DOMAIN set, they get https://<ref>.api.example.com, which needs a wildcard certificate (a DNS challenge).

Caddyfile
vulcbase.example.com {  reverse_proxy localhost:3000} api.example.com {  reverse_proxy localhost:3195}

Keep Postgres off the public internet unless you want customers to connect directly; remove its published port from the Compose file to keep it private.

Backups and upgrades

  • Two volumes hold your data. vulcbase-pg has the control plane and every project database; back it up with pg_dumpall or a disk snapshot. vulcbase-data has storage files, project backups and imports.
  • Paid plans get nightly project backups, restorable from each project's Backups page. Copy the data volume off the machine too.
  • To upgrade, pull the new version and run the Compose command again. The web container applies new migrations on start. Take a backup first.
  • Plan limits apply on your own server too. Accounts the administrator marks as unlimited have no limits.