fix(infra): frontend healthcheck on 127.0.0.1, not localhost #525

Merged
marcel merged 1 commits from fix/frontend-healthcheck-ipv4 into main 2026-05-11 18:52:31 +02:00
Owner

Summary

Nightly Main Deploy staging has been failing since the new alpine-based frontend production image landed (8b109349, 2026-05-10):

Container archiv-staging-frontend-1 is unhealthy

The app itself is fine. The healthcheck is broken.

Root cause

Inside node:20.19.0-alpine3.21, /etc/hosts resolves localhost only to ::1:

$ docker exec archiv-staging-frontend-1 getent hosts localhost
::1               localhost  localhost

SvelteKit's adapter-node binds to 0.0.0.0 (IPv4 only) — no IPv6 listener. So the healthcheck's wget http://localhost:3000/login connects to ::1:3000, gets Connection refused, exits 1, every 15s. After 10 retries the container is marked unhealthy and docker compose up --wait fails the deploy.

Verified on staging:

$ docker exec archiv-staging-frontend-1 wget -qO- http://127.0.0.1:3000/login >/dev/null; echo $?
0
$ docker exec archiv-staging-frontend-1 wget -qO- http://[::1]:3000/login >/dev/null 2>&1; echo $?
1

Fix

Switch the healthcheck URL to 127.0.0.1 — bypasses /etc/hosts and matches what Node actually listens on. One-character change.

Test plan

  • Merge → next nightly Main Deploy staging completes
  • After deploy: docker ps on hetzner shows archiv-staging-frontend-1 as (healthy)
  • docker inspect archiv-staging-frontend-1 --format '{{.State.Health.Status}}' returns healthy

🤖 Generated with Claude Code

## Summary Nightly `Main Deploy staging` has been failing since the new alpine-based frontend production image landed (8b109349, 2026-05-10): > Container archiv-staging-frontend-1 is unhealthy The app itself is fine. The healthcheck is broken. ## Root cause Inside `node:20.19.0-alpine3.21`, `/etc/hosts` resolves `localhost` only to `::1`: ``` $ docker exec archiv-staging-frontend-1 getent hosts localhost ::1 localhost localhost ``` SvelteKit's adapter-node binds to `0.0.0.0` (IPv4 only) — no IPv6 listener. So the healthcheck's `wget http://localhost:3000/login` connects to `::1:3000`, gets `Connection refused`, exits 1, every 15s. After 10 retries the container is marked unhealthy and `docker compose up --wait` fails the deploy. Verified on staging: ``` $ docker exec archiv-staging-frontend-1 wget -qO- http://127.0.0.1:3000/login >/dev/null; echo $? 0 $ docker exec archiv-staging-frontend-1 wget -qO- http://[::1]:3000/login >/dev/null 2>&1; echo $? 1 ``` ## Fix Switch the healthcheck URL to `127.0.0.1` — bypasses `/etc/hosts` and matches what Node actually listens on. One-character change. ## Test plan - [ ] Merge → next nightly `Main Deploy staging` completes - [ ] After deploy: `docker ps` on hetzner shows `archiv-staging-frontend-1` as `(healthy)` - [ ] `docker inspect archiv-staging-frontend-1 --format '{{.State.Health.Status}}'` returns `healthy` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
marcel added 1 commit 2026-05-11 18:50:23 +02:00
fix(infra): frontend healthcheck on 127.0.0.1, not localhost
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 2m53s
CI / OCR Service Tests (pull_request) Successful in 17s
CI / Backend Unit Tests (pull_request) Successful in 4m33s
CI / fail2ban Regex (pull_request) Successful in 40s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m0s
CI / Unit & Component Tests (push) Failing after 2m52s
CI / OCR Service Tests (push) Successful in 18s
CI / Backend Unit Tests (push) Successful in 4m23s
CI / fail2ban Regex (push) Successful in 39s
CI / Compose Bucket Idempotency (push) Successful in 1m0s
5f3529439a
The new alpine-based frontend production image (`node:20.19.0-alpine3.21`)
resolves `localhost` only to `::1` in /etc/hosts. SvelteKit's adapter-node
binds to 0.0.0.0 (IPv4 only), so `wget http://localhost:3000/login` from
inside the container connects to ::1 and gets "Connection refused" every
15s. Container goes unhealthy → `docker compose up --wait` fails → nightly
staging deploy fails. The app itself is fine.

Switching to 127.0.0.1 bypasses /etc/hosts and matches what Node actually
listens on.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
marcel merged commit 5f3529439a into main 2026-05-11 18:52:31 +02:00
marcel deleted branch fix/frontend-healthcheck-ipv4 2026-05-11 18:52:31 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#525