Skip to content

ADR-0004: Container healthchecks — add everywhere, verify-first, skip the un-probeable

  • Status: Accepted
  • Date: 2026-07-14

Context

Most containers reported Docker health none because their images define no HEALTHCHECK, so any container-status view (Arcane, Homarr, docker ps) showed blank health for ~26 of 40 containers. "Running" is not the same as "healthy," and a blank badge is uninformative.

Decision

Add a Docker HEALTHCHECK to every container that can support a self-contained probe, under these rules:

  • Verify first. The exact probe must be run inside the container and confirmed to exit 0 before it's written to the compose file. A wrong healthcheck marks a working container unhealthy and can block anything with depends_on: condition: service_healthy.
  • Use whatever the image actually has: an app health subcommand (/beszel health, diun healthcheck, redis-cli ping), curl/busybox wget against a real 2xx path, nc -z/python-socket/bash /dev/tcp for TCP-only or tool-less images.
  • Recreate per-service, never whole-stack (a whole-stack up -d can trip on intentionally- down services like the otbr with a disconnected radio). Revert any check that goes unhealthy.
  • Skip, don't fake. Services with no self-contained probe get no healthcheck rather than a misleading one: distroless continuwuity (no shell/tools, no health subcommand) and pure worker sidecars with no port (controller-watch, trmnl-nowplaying).

Consequences

  • Near-universal green health badges; end state 0 unhealthy across 22 added checks.
  • To give a distroless/tool-less service a check later, bake a static probe into a wrapper image or add a sidecar — don't invent a probe that can't run in-container.
  • Patterns and the full skip list: docs/runbooks/monitoring.md.