From 3810fe1a1e92342eb96e9b2f5436cdbad596aa64 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Mon, 27 Apr 2026 13:51:57 +0000 Subject: [PATCH] fix(distributed): worker container healthcheck always unhealthy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Dockerfile's HEALTHCHECK probes http://localhost:8080/readyz, which is the OpenAI API server port. When the same image runs as a worker, it listens on the gRPC base port (50051) and an HTTP file transfer server on port-1 (50050) — nothing on 8080 — so docker always reports the container as unhealthy. Add unauthenticated /readyz and /healthz endpoints to the worker's HTTP file transfer server, and override HEALTHCHECK_ENDPOINT for worker-1 in the distributed compose file. Disable the healthcheck for agent-worker since it is NATS-only and exposes no HTTP server. Signed-off-by: Ettore Di Giacinto Assisted-by: claude-code:claude-opus-4-7 Signed-off-by: Ettore Di Giacinto --- core/services/nodes/file_transfer_server.go | 15 +++++++++++++++ docker-compose.distributed.yaml | 9 +++++++++ 2 files changed, 24 insertions(+) diff --git a/core/services/nodes/file_transfer_server.go b/core/services/nodes/file_transfer_server.go index 611fa2974..1607b85a7 100644 --- a/core/services/nodes/file_transfer_server.go +++ b/core/services/nodes/file_transfer_server.go @@ -109,6 +109,21 @@ func StartFileTransferServerWithListener(lis net.Listener, stagingDir, modelsDir registerBackendLogHandlers(mux, token, ls) } + // Liveness/readiness probes — unauthenticated so container orchestrators + // (Docker HEALTHCHECK, k8s probes) can hit them without the bearer token. + // Reaching this point means the listener is bound and the mux is serving. + healthHandler := func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet && r.Method != http.MethodHead { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) + } + mux.HandleFunc("/readyz", healthHandler) + mux.HandleFunc("/healthz", healthHandler) + addr := lis.Addr().String() server := &http.Server{ Handler: mux, diff --git a/docker-compose.distributed.yaml b/docker-compose.distributed.yaml index 9b66d0d5f..778293c84 100644 --- a/docker-compose.distributed.yaml +++ b/docker-compose.distributed.yaml @@ -96,7 +96,12 @@ services: - BASE_IMAGE=ubuntu:24.04 command: - worker + # The image's default HEALTHCHECK targets the server's /readyz on 8080. + # Workers don't run the OpenAI API server — their HTTP file transfer + # server runs on the gRPC base port - 1 (50050 here) and exposes /readyz. + # Override the env var so the inherited HEALTHCHECK probes the right port. environment: + HEALTHCHECK_ENDPOINT: "http://localhost:50050/readyz" LOCALAI_SERVE_ADDR: "0.0.0.0:50051" LOCALAI_ADVERTISE_ADDR: "worker-1:50051" LOCALAI_ADVERTISE_HTTP_ADDR: "worker-1:50050" @@ -187,6 +192,10 @@ services: - | apt-get update -qq && apt-get install -y -qq docker.io >/dev/null 2>&1 exec /entrypoint.sh agent-worker + # The agent worker is NATS-only — no HTTP server to probe. Disable the + # image's inherited HEALTHCHECK so the container doesn't show unhealthy. + healthcheck: + disable: true environment: LOCALAI_NATS_URL: "nats://nats:4222" LOCALAI_REGISTER_TO: "http://localai:8080"