# OpenBao: the secret store every other stack with a credential reads from. # # Loopback only. It is published to 127.0.0.1 on the host, is not on proxy-net, # and sets no VIRTUAL_HOST and no LETSENCRYPT_HOST, so nothing outside the host # can reach it. That is the whole reason the compose_stack role does its vault # read from the host rather than from the control node. See docs/secrets.md. services: openbao: container_name: openbao # Pinned, per server/README.md — and more firmly than most: a restart seals # the vault and needs a manual unseal afterwards, so moving this tag is a # deliberate maintenance window rather than a routine update. image: docker.io/openbao/openbao:2.6.2 restart: unless-stopped ports: # The bind address matters more than the port. Without the 127.0.0.1 the # secret store is on every interface the host has. - "127.0.0.1:8200:8200" # The image's default command is `server -dev`, which starts an in-memory # vault with a known root token. Override it — but with the subcommand # alone: the entrypoint script already appends `-config=/openbao/config` # whenever the first argument is `server`, so passing that flag again here # loads the directory twice and OpenBao tries to bind the listener twice, # failing with "address already in use". command: server volumes: - ./config:/openbao/config:ro - ./data:/openbao/file environment: # The bao CLI defaults to https://127.0.0.1:8200, but this listener has # TLS disabled (openbao.hcl sets tls_disable = true), so every # `docker exec ... bao ...` would otherwise fail with a TLS handshake # error. Set here so every exec'd command inherits it without needing # its own -e BAO_ADDR. See docs/secrets.md. BAO_ADDR: http://127.0.0.1:8200 # ./config is mounted read-only, but the entrypoint's chown of # /openbao/config runs on every start whenever its uid does not match # openbao's, which it never will here since the payload is synced as # the deploy user — and that chown would fail against a read-only # mount anyway. The config chown is echo-guarded, but the entrypoint's # matching chowns of /openbao/logs and /openbao/file are not, and the # script runs under `set -e` as uid 100: an un-runnable-as-non-root # chown there would exit the container rather than just log noise. This # stack survives today only because openbao.yml's stack_dirs hands # data/ to 100:1000 before first start, so the stat short-circuits # every chown — SKIP_CHOWN makes that not load-bearing. SKIP_CHOWN: "true" # mlock, so the unsealed keyring is never paged out to disk. cap_add: - IPC_LOCK logging: driver: "json-file" options: max-size: "1m" max-file: "1"