From 193a958c8d18e2d237d4500d264fada10d30e52f Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Sat, 11 Jul 2026 07:12:06 -0700 Subject: [PATCH] fix(selfhost): retry the container health-status check, not just /ready (#5089) #5085 fixed the /ready HTTP probe to retry instead of single-attempt- failing, then immediately surfaced this second, previously-masked instance of the same bug on the very next deploy: the Docker container health-status check right below it is also a single, non-retrying check, and caught the container in Docker's own transient "starting" state (its healthcheck re-probes /ready on an independent interval/start_period schedule, so it can still be "starting" even after /ready itself already answered 2xx). Retries only through "starting" -- any other non-healthy/running status (unhealthy, exited, restarting, ...) fails immediately rather than burning the full retry budget on something that will not resolve on its own. Verified against both paths: starting -> healthy resolves via retry, and an immediate unhealthy status fails fast without waiting. --- scripts/selfhost-post-update-check.sh | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/scripts/selfhost-post-update-check.sh b/scripts/selfhost-post-update-check.sh index cd85d386c..6750f92fd 100755 --- a/scripts/selfhost-post-update-check.sh +++ b/scripts/selfhost-post-update-check.sh @@ -51,7 +51,23 @@ if [ "$ready" -ne 1 ]; then exit 1 fi -status="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_id" 2>/dev/null || true)" +# Same race as the /ready probe above, one layer down: Docker's OWN healthcheck (docker-compose.yml) +# reports "starting" until its FIRST probe completes, which can still be true here even though /ready +# already answered 2xx (the two checks run on independent schedules) -- a normal boot must not fail on +# a state that is expected to resolve on its own within seconds. Only "starting" is worth waiting +# through; any other non-healthy/running status (unhealthy, exited, restarting, ...) is a real problem +# and fails immediately rather than burning the full retry budget on something that will not resolve. +status="" +for _ in $(seq 1 "$READY_RETRIES"); do + status="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_id" 2>/dev/null || true)" + if [ "$status" = "healthy" ] || [ "$status" = "running" ]; then + break + fi + if [ "$status" != "starting" ]; then + break + fi + sleep "$READY_RETRY_DELAY_SECONDS" +done echo "selfhost post-update check: $SERVICE container status=$status" if [ "$status" != "healthy" ] && [ "$status" != "running" ]; then echo "error: expected healthy or running, got $status" >&2