The default path. A small Linux VPS with a public IP, ports 80 and 443 open to the world, and a DNS record pointing at it.
- Linux VPS (~2 GB RAM is enough; e.g. any provider's smallest x86 instance).
- Docker + Docker Compose v2 installed.
- A registered domain. A DNS A record (or AAAA, or both) at your
DOMAINvalue pointing to the VPS public IP. - Ports 80 and 443 reachable from the public internet (no host firewall, no proxy claiming them).
- A GitHub OAuth app whose callback URL is
https://<your-domain>/api/auth/callback/github. - A Voyage AI API key and an OpenRouter API key.
# 1. Clone onto the VPS.
git clone <your-fork-url> brain && cd brain
# 2. Configure environment.
cp .env.example .env
$EDITOR .env
# Required for prod (see docs/configuration.md):
# NEXTAUTH_URL=https://<your-domain>
# NEXTAUTH_SECRET, INTERNAL_API_SECRET, ADMIN_REINDEX_TOKEN (openssl rand -base64 32)
# GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
# ALLOWED_GITHUB_USER=<your-github-login>
# VOYAGE_API_KEY, OPENROUTER_API_KEY
# DOMAIN=<your-domain>
# NEXT_PUBLIC_DOMAIN=<your-domain>
# ACME_EMAIL=<your-email>
# VAULT_REPO=<git url>; VAULT_GIT_TOKEN=<PAT> if private
# 3. Bring it up.
docker compose up -d --build
# 4. Watch logs in this order until each is healthy.
docker compose logs -f web
docker compose logs -f api
docker compose logs -f caddy # certificate obtained via HTTP-01
docker compose logs -f vault-syncer # initial clone + reindex
# 5. Open https://<your-domain> and sign in.The bundled ops/caddy/Caddyfile issues certificates via the HTTP-01 challenge. Caddy listens on port 80 and Let's Encrypt fetches a token over HTTP. Nothing extra to configure — just make sure port 80 is open.
If you cannot expose port 80 (e.g. operating behind a NAT) or you want wildcard certs, switch to the DNS-01 challenge with Cloudflare:
- Create a Cloudflare API token (Profile → API Tokens → Create Token).
Permissions:
Zone:Zone:Read+Zone:DNS:Edit. Resources: limit to your zone. - Set
CF_API_TOKEN=...in.env. - Edit
ops/caddy/Caddyfileand uncomment thetls { dns cloudflare {env.CF_API_TOKEN} }block. docker compose restart caddy.
The included Caddy image already bundles caddy-dns/cloudflare; no rebuild needed.
Same compose file works on managed Docker hosts (Easypanel, Coolify, Dokku, etc.). One thing to be careful of: do not let the PaaS host proxy claim your DOMAIN — Caddy inside the stack needs to terminate TLS, otherwise you get cert conflicts. Most PaaSes let you mark a service as not having a domain assigned; do that for every service in this stack.
After deploy, run the pen-test checklist (skip the Tailnet-only exposure section — that's for the Tailscale path).