// docs
self-host quickstart
One compose stack, Traefik (sole ingress) + relay + Postgres + OpenBao + MinIO, 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 for the relay-stack service secrets viaopenssl rand: database passwords, JWT signing key, OpenBao tokens (root, companion, deployer), internal service tokens (broker, deployer, companion host, companion control, live-session channel), and the enumeration key. One secret it does not generate:MINIO_ROOT_PASSWORDdefaults to a dev value in compose, so set it explicitly before any production deploy. 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.