// 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

  1. Clone and enter the repo.

    git clone https://github.com/shiftedx/CozyLabs.git
    cd CozyLabs
  2. Generate your secrets.

    ./deploy/bootstrap.sh

    This writes deploy/.env (mode 600, gitignored) with high-entropy values for POSTGRES_PASSWORD, JWT_SIGNING_KEY, and OPENBAO_TOKEN via openssl rand. It’s idempotent — re-running leaves an existing .env untouched (pass --force to rotate). .env.example documents every variable; never put real secrets there.

  3. Bring the stack up.

    docker compose -f deploy/docker-compose.yml up -d --build

    This builds cozylabs-relay:dev locally (never pulled), runs migrations on boot (RUN_MIGRATIONS=1), provisions OpenBao Transit with a one-shot sidecar, and starts the relay behind Traefik.

  4. Check it’s alive.

    curl -H 'Host: relay.localhost' http://localhost/healthz

    Expect ok. The relay is reachable at relay.localhost through 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.