Neu veröffentlicht: E-Commerce mit Power Pages, Stripe & Analytics

· David Göschel · DevOps  · 4 minuten Lesezeit

Frontend reparieren wenn der nginx Healthcheck an localhost scheitert

Der Frontend Container war gebaut, lief und war trotzdem unhealthy. Die Ursache war eine kleine Alpine Falle: BusyBox Wget suchte localhost zuerst über IPv6 und nginx lauschte nur auf IPv4.

Der Frontend Container war gebaut, lief und war trotzdem unhealthy. Die Ursache war eine kleine Alpine Falle: BusyBox Wget suchte localhost zuerst über IPv6 und nginx lauschte nur auf IPv4.

Inhalt

nginx Healthcheck in Docker beheben wenn localhost auf IPv6 zeigt

Der Frontend Container lief. Der Build war erfolgreich. Trotzdem meldete Docker den Container als unhealthy.

Die Log Meldung war unspektakulär.

wget: can't connect to remote host: Connection refused

Genau solche Fehler nerven, weil sie so klein aussehen. Das eigentliche Problem steckt oft nicht in nginx selbst, sondern in der Art, wie der Healthcheck aufgerufen wird.

Warum localhost hier falsch war

Der Container basierte auf nginx:alpine. Darin steckt BusyBox wget. Und BusyBox behandelt localhost nicht immer so, wie man es im Kopf hat.

Im Container konnte localhost zuerst auf ::1 zeigen, also auf IPv6. nginx lauschte aber nur auf IPv4.

server {
    listen 80;
    root /usr/share/nginx/html;
    index index.html;
}

Für den Browser war das egal. Für den Healthcheck nicht.

Die saubere Korrektur

Ich habe zwei kleine Änderungen gemacht.

  1. Der Healthcheck nutzt jetzt 127.0.0.1 statt localhost.
  2. nginx lauscht zusätzlich auf IPv6.
healthcheck:
  test: ["CMD", "curl", "-sf", "http://127.0.0.1/"]
server {
    listen 80;
    listen [::]:80;
    root /usr/share/nginx/html;
    index index.html;
}

Damit ist der Check robust genug für den Container und trotzdem simpel genug, um ihn später noch zu verstehen.

Warum ich curl statt wget nehme

curl ist in nginx:alpine verfügbar und verhält sich in diesem Fall verlässlicher.

Das ist kein magischer Trick. Es ist nur die weniger fehleranfällige Variante für einen Healthcheck, der wirklich nur wissen will, ob der Webserver antwortet.

Ich will in einem Healthcheck keine Nebenlogik. Ich will nur eine klare Aussage:

  1. Antwortet nginx auf 127.0.0.1?
  2. Dann ist der Container healthy.
  3. Wenn nicht, ist etwas wirklich kaputt.

Was ich aus dem nginx Healthcheck und IPv6 Problem gelernt habe

Ein Healthcheck ist keine Nebensache. Er ist Teil der Architektur.

Wenn er falsch gebaut ist, erzeugt er falsche Signale. Dann sieht ein funktionierender Container kaputt aus und die Suche geht in die falsche Richtung.

Für mich ist die Regel jetzt klar.

  1. In Alpine Containern lieber explizit 127.0.0.1 verwenden.
  2. Bei nginx zusätzlich IPv6 aktivieren, wenn der Stack später sauber dual stack sein soll.
  3. Einen Healthcheck immer so schlicht wie möglich halten.

Frontend Healthcheck und Antwortpfad mit nginx im Docker Container

Das Bild steht für den sichtbaren Teil des Frontends. Der eigentliche Fehler lag aber tiefer. Ein kleiner Healthcheck sorgte dafür, dass der Container trotz funktionierender Anwendung als unhealthy erschien.

Alle Artikel der Serie

  1. Vision und Systemübersicht: Chrome Extension, RAG-Architektur, Projekthintergrund: Artikel lesen
  2. RAG-System Aufbau: Qdrant, Embeddings, Cosine-Ähnlichkeit in TypeScript: Artikel lesen
  3. AI Provider Abstraktion: Ollama vs. OpenAI, Interface-Design, kein Vendor-Lock-in: Artikel lesen
  4. Chrome Extension MV3: Drei isolierte Laufzeitkontexte, Message Passing, Strategy Pattern: Artikel lesen
  5. Docker Compose Strategie: Override-Pattern, von lokal zu Azure: Artikel lesen
  6. Ollama lokal vs. Docker: Die Entscheidung und ihre Konsequenzen: Artikel lesen
  7. Ollama Auto-Pull Entrypoint: Automatisiertes Modell-Setup beim Container-Start: Artikel lesen
  8. tsconfig und Vite: Node16 vs. bundler, warum Vite eigene Regeln hat: Artikel lesen
  9. Instagram Caption mit MutationObserver vollständig laden: Artikel lesen
  10. Chrome Extension Foundation mit Health-Dot und Retry-Queue: Artikel lesen
  11. Phase 2 Features: Shadow DOM Overlay, Tailwind v4, Duplicate Detection: Artikel lesen
  12. Race Condition bei der Plattformerkennung: Wie ein UI-Event die Instagram-Erkennung bricht: Artikel lesen
  13. PostId-Extraktion in zwei Instagram-Layouts: querySelector vs. Ancestor-Traversal: Artikel lesen
  14. Instagram Karussell vollständig erfassen mit MutationObserver: Lazy-Loading, Observer-before-click, Timeout-Fallback: Artikel lesen
  15. Notiz und Tags beim Screenshot-Speichern: Artikel lesen
  16. Instagram Tastatur-Shortcuts blockieren Chrome Extension Eingaben: Artikel lesen
  17. Lowercase-Normalisierung und Duplikat-Erkennung im Tag-Input: Artikel lesen
  18. Zitadel Login V2 in Docker Compose: drei versteckte Fehler: Artikel lesen
  19. PKCE OAuth in einer Chrome MV3 Extension: Artikel lesen
  20. React Frontend mit react-oidc-context und Zitadel: Artikel lesen
  21. Vite Build-Time-Umgebungsvariablen in Docker: Artikel lesen
  22. Event-Driven Ingestion mit BullMQ und Redis: Artikel lesen
  23. MinIO statt Azurite: S3-kompatible Objektspeicherung lokal und auf Hetzner: Artikel lesen
  24. access_token, id_token und der Userinfo-Endpoint: was wohin gehört: Artikel lesen
  25. Qdrant Multi-Tenancy: Pro Nutzer eine eigene Collection: Artikel lesen
  26. Wenn Backend und Frontend unterschiedliche Typen kennen: Artikel lesen
  27. Zitadel Bootstrap entfernt: Host-Header-Bug und manuelles Setup: Artikel lesen
  28. Backend Code Review: sechs Probleme vor dem Launch behoben: Artikel lesen
  29. Traefik statt NGINX: Reverse Proxy für einen wachsenden Docker-Compose-Stack: Artikel lesen
  30. Zweischichtiges Rate Limiting: Traefik und express-rate-limit mit Redis: Artikel lesen
  31. DSGVO Art. 17 korrekt implementieren: Promise.allSettled und Export-Batching: Artikel lesen
  32. Embedding-Modell-Lock-in: Warum mxbai-embed-large eine Produktionsentscheidung für immer ist: Artikel lesen
  33. Docker Volumes in Produktion: Named Volumes, Bind Mounts und der Hetzner-Volume-Trick: Artikel lesen
  34. Zwei Sicherheitslücken vor dem Launch: Redis ohne Auth und ein offener Qdrant-Admin-Port: Artikel lesen
  35. Traefik als einziger Einstiegspunkt im Docker Compose Stack: Artikel lesen
  36. Zitadel hinter Traefik richtig verdrahten mit Issuer, JWKS und Login V2: Artikel lesen
  37. Frontend reparieren wenn der nginx Healthcheck an localhost scheitert: (dieser Artikel)
  38. Observability für meinen Docker Compose Stack mit Bull Board und Dozzle: Artikel lesen
  39. Qdrant Dashboard sicher öffnen mit lokalem Traefik und SSH Tunnel: Artikel lesen
  40. Diagnose: Warum mein Chunking trotz Tokenisierung noch scheiterte: Artikel lesen
  41. Entscheidung: Warum ich den Chunk auf 1500 Tokens gesetzt habe: Artikel lesen
  42. Implementierung: Wie ich den Embedding Workflow in mehrere saubere Schritte zerlegt habe: Artikel lesen
  43. Validierung: Wie ich Chunking, Speicherung und Suche wieder zusammenbringe: Artikel lesen

Du baust gerade einen ähnlichen Multi-Service-Stack und fragst dich, wie du Routing und TLS sauber löst? Lass uns das gemeinsam einschätzen.

Zurück zum Blog

Ähnliche Beiträge

Alle Beiträge ansehen