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
| Part | Role | Port |
|---|---|---|
| Postgres 17 | Holds the control database (accounts, projects, plans) and one database per project. Needs a superuser and logical WAL (for realtime). | 5432 |
| web | The dashboard, this website and the control API. Applies its own migrations when it starts and creates the administrator account. | 3000 |
| gateway | The 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) andapi.example.com(project APIs), and a reverse proxy that terminates TLS in front of ports 3000 and 3195. - Optionally a wildcard record
*.api.example.comand 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 --buildThe Compose file looks like this (shortened):
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
| Variable | What it is |
|---|---|
DATABASE_URL | Connection string of the control-plane database. Set by the Compose file. |
CLUSTER_ADMIN_URL | Superuser connection to the cluster that holds project databases. Used to create and drop them. Set by the Compose file. |
APP_SECRET | Long random string that encrypts project secrets at rest. Never change it once projects exist. |
APP_URL | Public address of the dashboard. |
GATEWAY_URL | Public address of the gateway. Projects are served at GATEWAY_URL/p/<ref> unless a wildcard domain is set. |
API_WILDCARD_DOMAIN | Optional. When set, each project is served at https://<ref>.<domain>. |
PUBLIC_DB_HOST | Optional. The host:port shown in connection strings, when you expose Postgres for psql and ORMs. |
ADMIN_EMAIL, ADMIN_PASSWORD | The first administrator, created at startup. |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM | Outgoing mail for account and auth emails. |
DATA_DIR | Where backups, imports and storage files are kept. Mount a volume here. Set by the Compose file. |
PG_BIN_DIR | Folder with pg_dump, pg_restore and psql (version 17). Empty uses the PATH. |
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
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).
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-pghas the control plane and every project database; back it up withpg_dumpallor a disk snapshot.vulcbase-datahas 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.