Problem
Hit twice today during live deploys: `selfhost-post-update-check.sh` probes `/ready` exactly once, immediately after `selfhost-update.sh` calls `deploy-selfhost-prebuilt.sh`. `docker compose up -d` returns as soon as the container starts, well before the app inside has finished booting and bound its port — so the single `curl -sf` reliably fails on a completely normal deploy, even though the container becomes `healthy` seconds later.
The `gittensory` service's own Docker healthcheck (`docker-compose.yml`) already documents and tolerates this: `start_period: 60s` "tolerates the Postgres cold start." The post-update script had no equivalent tolerance.
Fix
Replace the single probe with a bounded retry loop (default 45 attempts × 2s = 90s, comfortably over the documented 60s `start_period`), configurable via `SELFHOST_READY_RETRIES`/`SELFHOST_READY_RETRY_DELAY_SECONDS` for an operator with an unusually slow cold start.
Problem
Hit twice today during live deploys: `selfhost-post-update-check.sh` probes `/ready` exactly once, immediately after `selfhost-update.sh` calls `deploy-selfhost-prebuilt.sh`. `docker compose up -d` returns as soon as the container starts, well before the app inside has finished booting and bound its port — so the single `curl -sf` reliably fails on a completely normal deploy, even though the container becomes `healthy` seconds later.
The `gittensory` service's own Docker healthcheck (`docker-compose.yml`) already documents and tolerates this: `start_period: 60s` "tolerates the Postgres cold start." The post-update script had no equivalent tolerance.
Fix
Replace the single probe with a bounded retry loop (default 45 attempts × 2s = 90s, comfortably over the documented 60s `start_period`), configurable via `SELFHOST_READY_RETRIES`/`SELFHOST_READY_RETRY_DELAY_SECONDS` for an operator with an unusually slow cold start.