Troubleshooting
Real failure modes documented in this repository — the first-boot password, AppArmor, BuildKit, the read-only secrets volume, and gateway 401s.
Where is the first-boot admin password?
Printed once, on the boot that creates the account, to the control plane’s log — and nowhere else:
docker compose logs control-plane | grep -A6 'FIRST-BOOT ADMIN'
If DSH_ADMIN_PASSWORD was set before that first boot, nothing is ever printed — that password is the one to use.
Postgres won’t start on a Proxmox or Debian host
Recorded failures on these hosts include AppArmor blocking Postgres’ Unix socket. The following override disables AppArmor confinement for the affected containers; use it only after confirming the cause and reviewing its security impact:
# docker-compose.override.yml
services:
postgres:
security_opt: ["apparmor=unconfined"]
control-plane:
security_opt: ["apparmor=unconfined"]
gateway:
security_opt: ["apparmor=unconfined"]
This override applies to affected host configurations; it is not a requirement for every deployment. See Docker’s AppArmor documentation for profile behaviour.
Building an image fails with no network access
Recorded failures on Proxmox or Debian Docker hosts include unavailable network access from the default BuildKit builder’s RUN steps. If this explains a network-dependent build failure, use the classic builder with the host’s network:
DOCKER_BUILDKIT=0 docker build --network=host …
A service can’t read a file from the secrets volume
The secrets volume is mounted read-only everywhere except the service that generates it — by design, so nothing else can rotate a secret out from under the rest of the stack. If a service can’t read a file it expects there, check that the first-boot init service completed successfully rather than adjusting permissions on the reader.
Every Gateway call returns 401
The control plane and the Gateway are handed the same token and refuse anything that doesn’t match, byte for byte — including a trailing newline. A token file written with an extra newline at the end produces exactly this: every RPC 401s, and neither log says why, because from the Gateway’s side an unrecognized token and a mistyped one look identical. Compare the token files directly rather than assuming the value is wrong.
Documentation