docker: give the image a healthcheck it can actually run - #941
Open
m4bard wants to merge 1 commit into
Open
Conversation
The runtime image has never declared a HEALTHCHECK, so a Listenarr container reports no health at all and anything that gates on container health has nothing to read. An operator who adds one reaches for the obvious curl -f http://localhost:4545/, and that cannot work here. The runtime stage pulls curl in only as a build dependency of the Discord bot's Node install and then purges it again, and wget was never present. The check exits 127 on every run, so the container sits permanently unhealthy while the application serves normally. An orchestrator that restarts on unhealthy will act on that, and a real outage becomes indistinguishable from the standing false one. Node is in the final image deliberately, because the Discord bot runtime needs it and the install script keeps the node binary after stripping npm. So the request can be issued without adding anything to the image: node -e with http.get, exiting 0 only on a 200. The check asks the application's own readiness probe, GET /api/v1/system/ready, rather than /. That endpoint is AllowAnonymous, so it answers without a session, and it already returns 503 until the database is connected, migrations are current and the filesystem is ready. That is the condition worth reporting as health. A request to / would only show that the static file middleware is mounted. Both Dockerfiles get the instruction. listenarr.api/Dockerfile.runtime is the one CI publishes; the root Dockerfile is the local build, and leaving it out would let the two disagree about what a healthy container means. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #940.
Adds a
HEALTHCHECKto both Dockerfiles. 14 lines added, nothing removed, no application code touched.#940 carries the reasoning. In brief, on each choice:
The command runs
noderather than pulling in a package. The runtime ships no HTTP client, so a curl or wget check would mean putting one back.Dockerfile.runtime:36records that node is kept deliberately for the Discord bot ("the bot only needs node"), so the request goes through that binary instead. It adds no package and no layer.It asks
/api/v1/system/readyrather than/. That endpoint is[AllowAnonymous], so it answers with authentication enabled, and it returns 200 or 503 off real database, migration and filesystem state. A request to/would only prove the static file middleware is mounted.Both Dockerfiles get it. The publish workflow builds
listenarr.api/Dockerfile.runtime; the rootDockerfileis the local build. Patching one and not the other would let the two disagree about what a healthy container means.On the intervals: 30s apart with a 5s timeout is cheap for one local HTTP request. The 90s start period is the one worth explaining. Failures inside that window do not count against the retry budget, and a first boot with migrations to run can take a while, so a generous window keeps a slow start from being reported as unhealthy.
If you build this with podman
Podman's default OCI build format drops
HEALTHCHECK. It does warn, but the warning is easy to miss, so a locally built image looks as though this change did nothing. Build with--format dockerand the instruction survives.The published image is unaffected. CI builds through buildx, and the config blob of another buildx-built image using the same OCI media types from the same registry carries a populated
Healthcheck.What was checked
By running, against stock
ghcr.io/listenarrs/listenarr:canary:curl,wget,ncandbusyboxare all absent, and the naive curl healthcheck exits 127.Status=healthywithFailingStreak=0.I have not run this in production. The evidence above comes from a test container.
Worked through with Claude Code at my direction. The image contents and both exit branches were checked by running them against the published image, and I watched a container reach healthy before opening this. I reviewed this before posting.