fix(compose): app healthcheck uses 127.0.0.1 not localhost (fixes false unhealthy) #5

Merged
jknapp merged 1 commits from fix/app-healthcheck-ipv4 into main 2026-06-12 19:19:47 +00:00
Owner

What

The app service healthcheck probes http://localhost:3000/api/health. Inside the container, localhost resolves to ::1 (IPv6) first per /etc/hosts, but the Next.js standalone server listens only on 0.0.0.0 (IPv4). The probe hits ::1:3000, gets Connection refused, and the container is reported unhealthy — even though the app serves HTTP 200 on both / and /api/health.

This is why shared-memory-app-1 showed (unhealthy) for weeks while functioning normally.

Fix

One-line change: probe http://127.0.0.1:3000/api/health instead of localhost, matching the interface the app actually binds. The db and embedder healthchecks already avoid localhost.

Verification (on production)

  • docker exec ... wget http://localhost:3000/api/health -> Connection refused (::1)
  • docker exec ... wget http://127.0.0.1:3000/api/health -> HTTP 200
  • container /etc/hosts: ::1 localhost
  • app listen socket: 0.0.0.0:3000 (IPv4 only)

No runtime/app code changes; healthcheck only. Takes effect on next docker compose up -d (container recreate).

## What The `app` service healthcheck probes `http://localhost:3000/api/health`. Inside the container, `localhost` resolves to `::1` (IPv6) first per `/etc/hosts`, but the Next.js standalone server listens only on `0.0.0.0` (IPv4). The probe hits `::1:3000`, gets `Connection refused`, and the container is reported **unhealthy** — even though the app serves HTTP 200 on both `/` and `/api/health`. This is why `shared-memory-app-1` showed `(unhealthy)` for weeks while functioning normally. ## Fix One-line change: probe `http://127.0.0.1:3000/api/health` instead of `localhost`, matching the interface the app actually binds. The `db` and `embedder` healthchecks already avoid `localhost`. ## Verification (on production) - `docker exec ... wget http://localhost:3000/api/health` -> Connection refused (`::1`) - `docker exec ... wget http://127.0.0.1:3000/api/health` -> HTTP 200 - container `/etc/hosts`: `::1 localhost` - app listen socket: `0.0.0.0:3000` (IPv4 only) No runtime/app code changes; healthcheck only. Takes effect on next `docker compose up -d` (container recreate).
jknapp added 1 commit 2026-06-12 19:16:42 +00:00
Inside the app container localhost resolves to ::1 (IPv6) first, but the
Next.js standalone server listens only on 0.0.0.0 (IPv4). The healthcheck
probed http://localhost:3000/api/health and got Connection refused on ::1,
so the container reported unhealthy for weeks despite serving 200 on both
/ and /api/health. Switch the probe to 127.0.0.1 to match the bound iface.

The db and embedder healthchecks already avoid localhost.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Owner

Code review

No issues found. Checked for correctness and consistency.

Verified the IPv4/IPv6 reasoning is sound: the Dockerfile sets HOSTNAME=0.0.0.0, so the Next.js standalone server binds IPv4-only; an in-container localhost probe hits ::1 first and is refused. 127.0.0.1 is the correct fix. Confirmed APP_BIND only affects the host-side port publish, not the in-container bind, so the probe is always reachable. The change is consistent with the existing embedder healthcheck (also 127.0.0.1), and the CMD-SHELL string is syntactically clean.

🤖 Generated with Claude Code

### Code review No issues found. Checked for correctness and consistency. Verified the IPv4/IPv6 reasoning is sound: the Dockerfile sets `HOSTNAME=0.0.0.0`, so the Next.js standalone server binds IPv4-only; an in-container `localhost` probe hits `::1` first and is refused. `127.0.0.1` is the correct fix. Confirmed `APP_BIND` only affects the host-side port publish, not the in-container bind, so the probe is always reachable. The change is consistent with the existing `embedder` healthcheck (also `127.0.0.1`), and the CMD-SHELL string is syntactically clean. 🤖 Generated with [Claude Code](https://claude.ai/code)
jknapp merged commit 41149fe709 into main 2026-06-12 19:19:47 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: cybercove-labs/shared-memory#5