// docs
self-host quickstart
One compose stack — Traefik (sole ingress) + relay + Postgres + OpenBao — that runs the same for local validation and production; the two differ only by domain, TLS, and where the volumes live.
prerequisites
- Docker with Compose v2
openssl(used by the bootstrap script to generate secrets)- a box with a couple of GB of RAM to spare
the quickstart
Clone and enter the repo.
git clone https://github.com/shiftedx/CozyLabs.git cd CozyLabsGenerate your secrets.
./deploy/bootstrap.shThis writes
deploy/.env(mode 600, gitignored) with high-entropy values forPOSTGRES_PASSWORD,JWT_SIGNING_KEY, andOPENBAO_TOKENviaopenssl rand. It’s idempotent — re-running leaves an existing.envuntouched (pass--forceto rotate)..env.exampledocuments every variable; never put real secrets there.Bring the stack up.
docker compose -f deploy/docker-compose.yml up -d --buildThis builds
cozylabs-relay:devlocally (never pulled), runs migrations on boot (RUN_MIGRATIONS=1), provisions OpenBao Transit with a one-shot sidecar, and starts the relay behind Traefik.Check it’s alive.
curl -H 'Host: relay.localhost' http://localhost/healthzExpect
ok. The relay is reachable atrelay.localhostthrough Traefik over HTTP.
Tear down (and wipe volumes): docker compose -f deploy/docker-compose.yml down -v.
two networks, by design
Postgres and OpenBao join only the internal Docker network (internal: true) and publish no
host ports — they are unreachable from the host or the internet. Traefik is the only container
that publishes a port. This is the SO-6 boundary: the data plane simply isn’t there to attack.
going to production
Production is the same base stack plus one compose override that adds TLS/ACME, real domains, and a properly initialized (non-dev) OpenBao:
./deploy/bootstrap.sh # base secrets, then add *_DOMAIN / ACME_EMAIL vars
./deploy/render-prod-config.sh # renders Traefik host rules from deploy/.env
./deploy/preflight-prod.sh # static check — fails fast on misconfig
docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.prod.yml up -d
./deploy/validate-prod.sh # dynamic check against the live deployment
The full runbook — DNS, the production OpenBao unseal ceremony, the scoped Transit token, the Cloudflare Tunnel shape for zero published ports — lives in architecture → deploy.
next
Create your account and your first sealed project, then deploy an agent onto the board.