curl Wait-Until-Ready: Health Check Loop
Waiting for a service to boot needs a bounded poll loop, not a fixed sleep.
Spinning up a service in CI and then hitting it immediately races the boot. A poll-until-healthy loop is the reliable pattern.
What it does
A readiness loop repeatedly probes a health endpoint until it returns a success status, then continues, or gives up after a maximum number of attempts so the job fails instead of hanging. curl provides the probe; the loop and timeout live in the shell. Using -f makes a non-200 a failure that the loop can detect.
Common usage
# poll up to ~60s for a 200, then fail the step
for i in $(seq 1 30); do
if curl -fsS -o /dev/null --max-time 5 http://localhost:8080/health; then
echo "service ready"; exit 0
fi
echo "waiting ($i)..."; sleep 2
done
echo "service did not become healthy"; exit 1Flags
| Piece | What it does |
|---|---|
| -fsS | Fail on >= 400, quiet progress, show errors |
| -o /dev/null | Discard the body; we only care about success |
| --max-time 5 | Bound each probe so a hang does not stall the loop |
| sleep / seq | Backoff and a bounded number of attempts |
| --retry (alt) | curl can self-retry, but a loop gives clearer control |
In CI
Prefer a loop with an attempt cap over a single long sleep: it is faster on the happy path and fails deterministically when the service never starts. Put --max-time on each probe so one slow attempt does not eat the whole budget. Log the attempt number so a flaky boot is visible.
Common errors in CI
curl: (7) Failed to connect to localhost port 8080: Connection refused on early attempts is normal while the service boots; the loop retries. If it never succeeds, the final exit 1 fails the step, which is the intended behavior.