From 5c667fb4f85b25a162feb7e14f48c024f689492d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20L=C3=BCtzner?= Date: Mon, 21 Sep 2026 11:42:29 +0200 Subject: [PATCH] Let the Mercure image say whether the Mercure image is healthy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to eca0f8b7, which pointed our healthcheck at the hub's new readiness path. Correct, and still the wrong shape: it leaves plMail asserting something about the internals of an image that is deliberately unpinned so it can update itself. The next time the hub moves that path, CI goes red for another three days. The image has always shipped its own HEALTHCHECK — wget against 127.0.0.1:2019/mercure/health/ready, 60s grace, 5s timeout, 5 retries — which is the same grace and retries compose.yaml was spelling out by hand. Inheriting it costs nothing there and cannot drift, because it travels with the image it describes. The test stack wanted to keep its five-second interval and inherit the command, and Compose refuses: a healthcheck block without a `test` is rejected with "must define a healthcheck command for all healthchecks". So owning the cadence means owning the command again, which is the thing that rotted. It asks every 30s now — a few more seconds before a job starts, against never again waiting for ever. Verified against the current image: the running container carries the image's healthcheck verbatim and reports healthy, and the test stack comes up. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 7 ++++--- compose.test.yaml | 23 ++++++++++++----------- compose.yaml | 22 +++++++++++----------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f409ddcf..26249f10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,9 +16,10 @@ The published image tags: `latest` follows the most recent release below, and the only symptom was `(unhealthy)` in `docker ps` and in the TrueNAS view — alarming, and about nothing. The test stack does wait on it, so CI stopped being able to start at all. - The check now asks the hub where the hub actually answers, which is the path its own built-in - healthcheck has always used. Overriding that with a URL of our own is what let the two drift - apart in the first place. + plMail no longer describes the hub's health itself: the image's own check is used, which moves + when the image moves. The hub is deliberately not pinned to a version so that it keeps itself up + to date, and that is exactly why nothing about its internals should be written down here — + anything hard-coded about an image that updates itself is a thing waiting to rot. ## v0.2.29 — 2026-09-21 diff --git a/compose.test.yaml b/compose.test.yaml index cd72f8e8..08aecfda 100644 --- a/compose.test.yaml +++ b/compose.test.yaml @@ -187,17 +187,18 @@ services: SERVER_NAME: ":80" MERCURE_PUBLISHER_JWT_KEY: e2e-mercure-secret-not-for-production MERCURE_SUBSCRIBER_JWT_KEY: e2e-mercure-secret-not-for-production - healthcheck: - # The ADMIN endpoint, not "/healthz" on the hub port, and not a URL of - # our own choosing. The hub moved its readiness probe: "/healthz" now - # answers 404, `curl -f` treats that as failure, and the container is - # marked unhealthy for ever while serving perfectly. The image's own - # HEALTHCHECK has always used this path — overriding it with a guess is - # what let the two drift apart, and the guess is the half that rotted. - test: ["CMD", "curl", "-f", "http://127.0.0.1:2019/mercure/health/ready"] - interval: 5s - timeout: 5s - retries: 10 + # NO healthcheck here, deliberately — the image's own is used, exactly as + # in compose.yaml and for the same reason: the hub moved its readiness + # probe out from under the URL we had hard-coded, and every job in CI then + # failed to start for three days while the hub itself was fine. + # + # Tempting to keep just the old five-second interval and inherit the rest, + # and Compose will not have it: a healthcheck block without a `test` is + # rejected outright — "must define a healthcheck command for all + # healthchecks" — so the only way to own the cadence is to own the command + # again, which is the thing that rotted. The image asks every 30s instead + # of every 5s, so a job waits a few more seconds for the hub and no longer + # risks waiting for ever. app: <<: *test-app diff --git a/compose.yaml b/compose.yaml index 6b85d3d4..4525db8f 100644 --- a/compose.yaml +++ b/compose.yaml @@ -299,17 +299,17 @@ services: depends_on: secrets-init: condition: service_completed_successfully - healthcheck: - # The ADMIN endpoint, not "/healthz" on the hub port, and not a URL of - # our own choosing. The hub moved its readiness probe: "/healthz" now - # answers 404, `curl -f` treats that as failure, and the container is - # marked unhealthy for ever while serving perfectly. The image's own - # HEALTHCHECK has always used this path — overriding it with a guess is - # what let the two drift apart, and the guess is the half that rotted. - test: ["CMD", "curl", "-f", "http://127.0.0.1:2019/mercure/health/ready"] - timeout: 5s - retries: 5 - start_period: 60s + # NO `test:` HERE ON PURPOSE — the image's own HEALTHCHECK is used. + # + # We used to spell one out, aimed at "/healthz", and the hub moved its + # readiness probe out from under it: "/healthz" answers 404 now, `curl + # -f` reads a 404 as failure, and this container reported itself unhealthy + # for ever while serving every subscription put to it. The image is + # deliberately on a floating tag so it keeps up to date by itself, which + # means anything we hard-code about its internals is a thing waiting to + # rot. Its own check — wget against 127.0.0.1:2019/mercure/health/ready, + # 60s grace, 5 retries — moves when the image moves, which is the whole + # point of not pinning it. volumes: - mercure_data:/data - mercure_config:/config