Add API healthcheck and deepen /health to probe DB + Redis #97
Labels
No labels
bug
duplicate
enhancement
future
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/TeaLeaves#97
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: Medium · Confidence: High · Effort: S · Category: ops
Evidence
docker-compose.yml- api service hasrestart: unless-stopped, nohealthcheck; appdepends_on: apiwith no condition.api/src/app.ts:81-83-/healthreturns{status:'ok'}unconditionally; no DB/Redis probe.api/Dockerfile- noHEALTHCHECK.Problem
A wedged-but-alive API (pg pool exhausted, event loop blocked, e.g. by F-09) is never detected or restarted;
restart: unless-stoppedonly reacts to process exit./healthcan't distinguish "up" from "up but DB down."Impact
Silent brownouts with no auto-recovery.
Fix
Add a compose healthcheck hitting
/health; deepen/healthto runSELECT 1+ RedisPINGwith a short timeout; gateapponservice_healthy.Acceptance criteria
/healthreturns non-200 when DB or Redis is unreachable.Related: F-09 (backup hang can wedge the event loop).
Filed from the 2026-07-15 codebase audit. Full report:
docs/.internal/report-2026-07-15.md(gitignored).Done in
109c53a(v7.2.0) — with one criterion that cannot be met as written; details below./healthnow probes its dependenciesSELECT 1on the pg pool and a RedisPING, each wrapped in a 2 s timeout so a hung dependency can't hang the endpoint itself. Returns 503 with per-dependency detail. Verified live on the dev server by stopping Redis:Two things worth recording:
/healthwas registered afterdefaultRateLimit. A container healthcheck polls continuously; a 429 would be read as "unhealthy" and could restart-loop a perfectly good container. It's now registered before the rate limiter — the limiter must never be able to fail the healthcheck.curland nowget(the Dockerfile purges curl after fetching the PGDG key), so the conventionalHEALTHCHECK CMD curl -f localhost:3000/healthwould never have worked here. TheHEALTHCHECKuses node's globalfetchinstead — no new packages, no image bloat.appis now gated oncondition: service_healthy, so nginx stops proxying to an API that's still running migrations. Visible in the deploy output:Container tealeaves-api-1 Healthybefore app starts.Acceptance criteria
/healthreturns non-200 when DB or Redis is unreachable.Compose restarts the api container when the healthcheck fails.— not achievable; the premise is wrong.Docker Compose does not restart containers on healthcheck failure.
restart:reacts to the process exiting; acting on health status is Swarm/Kubernetes behaviour, and there is norestart_on_unhealthyin Compose. Verified rather than assumed — stopped Redis and waited out the 3 retries:Detected, marked unhealthy, never restarted.
Closing because the title and the substantive work are done — the probe, the
HEALTHCHECK, and thedepends_ongating. Auto-recovery is split out to #137, which needs an infrastructure decision rather than more code: the usual answer (an autoheal sidecar) wants/var/run/docker.sockmounted, i.e. root-equivalent access on a host running ~55 other containers — not a trade to make silently for a rare brownout. Four options laid out there.CI green on re-run of
109c53a(249/249). Deployed to dev, container reportinghealthy.