From 741ed4764dd711d313e90ba903bdb821c970d6df Mon Sep 17 00:00:00 2001 From: Alfredo Di Stasio Date: Tue, 30 Jun 2026 10:53:01 +0200 Subject: [PATCH] feat: add backend health checks --- README.md | 10 ++++++++++ config/dynamic/azionelab.yml | 8 ++++++++ config/dynamic/lab-younerd.yml | 8 ++++++++ docs/adr/0001-traefik-tcp-tls-passthrough.md | 2 +- docs/operations.md | 5 ++++- docs/runbook.md | 11 +++++++++++ docs/testing.md | 1 + 7 files changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 81a88fb..efc549c 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,11 @@ http: services: lab-younerd-http: loadBalancer: + healthCheck: + path: "/" + hostname: "lab.younerd.org" + interval: "10s" + timeout: "3s" servers: - url: "http://127.0.0.1:8080" @@ -43,6 +48,9 @@ tcp: services: lab-younerd-tls: loadBalancer: + healthCheck: + interval: "10s" + timeout: "3s" servers: - address: "127.0.0.1:8443" ``` @@ -59,6 +67,8 @@ www.azionelab.org -> 127.0.0.1:9080 Adjust `8080` and `9080` to the local ports where the backend HTTP service is listening. The full HTTP request is forwarded so the backend can handle redirects and ACME challenge paths. +Each backend service includes health checks. TCP checks verify that the TLS port accepts connections; HTTP checks call `/` with the configured hostname and treat successful or redirect responses as healthy. + ## Run Validate the Compose file: diff --git a/config/dynamic/azionelab.yml b/config/dynamic/azionelab.yml index 6bef6c0..f44f8ed 100644 --- a/config/dynamic/azionelab.yml +++ b/config/dynamic/azionelab.yml @@ -9,6 +9,11 @@ http: services: azionelab-http: loadBalancer: + healthCheck: + path: "/" + hostname: "azionelab.org" + interval: "10s" + timeout: "3s" servers: - url: "http://127.0.0.1:9080" @@ -25,5 +30,8 @@ tcp: services: azionelab-tls: loadBalancer: + healthCheck: + interval: "10s" + timeout: "3s" servers: - address: "127.0.0.1:9443" diff --git a/config/dynamic/lab-younerd.yml b/config/dynamic/lab-younerd.yml index 4c2f81c..252307b 100644 --- a/config/dynamic/lab-younerd.yml +++ b/config/dynamic/lab-younerd.yml @@ -9,6 +9,11 @@ http: services: lab-younerd-http: loadBalancer: + healthCheck: + path: "/" + hostname: "lab.younerd.org" + interval: "10s" + timeout: "3s" servers: - url: "http://127.0.0.1:8080" @@ -25,5 +30,8 @@ tcp: services: lab-younerd-tls: loadBalancer: + healthCheck: + interval: "10s" + timeout: "3s" servers: - address: "127.0.0.1:8443" diff --git a/docs/adr/0001-traefik-tcp-tls-passthrough.md b/docs/adr/0001-traefik-tcp-tls-passthrough.md index 1a3b101..e1cc845 100644 --- a/docs/adr/0001-traefik-tcp-tls-passthrough.md +++ b/docs/adr/0001-traefik-tcp-tls-passthrough.md @@ -20,7 +20,7 @@ Traefik routes incoming TLS connections with `HostSNI(...)` to the configured TC TLS certificates, private keys, and certificate renewal remain the responsibility of each backend. Traefik can route TLS traffic based on the TLS SNI value but cannot inspect HTTP headers or paths because TLS is passed through unchanged. -Backend health is limited to TCP reachability unless protocol-specific health checks are added later. Configuration changes in `config/dynamic/` are watched by Traefik and can be applied without recreating the container. +Backend health checks are configured per site. TCP health checks verify backend port reachability; HTTP health checks call `/` with the configured hostname. Configuration changes in `config/dynamic/` are watched by Traefik and can be applied without recreating the container. ## Alternatives considered diff --git a/docs/operations.md b/docs/operations.md index fe733af..28bed51 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -24,7 +24,7 @@ Dynamic configuration changes under `config/dynamic/` are watched by Traefik and ## Health checks -The container healthcheck runs: +The Traefik container healthcheck runs: ```bash traefik healthcheck --ping @@ -36,6 +36,8 @@ Check health with: docker compose ps ``` +Backend services also define Traefik load-balancer health checks in each file under `config/dynamic/`. HTTP checks call `/` with the configured hostname; TCP checks verify that the local TLS port accepts connections. + ## Logs Traefik logs to container stdout: @@ -58,3 +60,4 @@ For HTTP on port `80`, update the HTTP services in the relevant site file under - Backend certificates and renewal jobs must be monitored on each backend. - SNI routing depends on clients sending the expected hostname during the TLS handshake. Clients that connect by IP address or omit SNI will not match hostname-specific routers. - Public port `80/tcp` forwards all HTTP paths for configured hostnames to the matching backend. +- TCP health checks verify reachability, not certificate validity or application-level correctness. diff --git a/docs/runbook.md b/docs/runbook.md index 9538096..a49e42f 100644 --- a/docs/runbook.md +++ b/docs/runbook.md @@ -39,6 +39,17 @@ docker compose logs traefik Confirm that `config/traefik.yml` is mounted and that the ping entrypoint is enabled. +### Backend is unhealthy + +Check Traefik logs and verify that the local backend ports respond from the host: + +```bash +curl -H 'Host: lab.younerd.org' http://127.0.0.1:8080/ +openssl s_client -connect 127.0.0.1:8443 -servername lab.younerd.org +``` + +Repeat with the hostname and ports from the relevant file under `config/dynamic/`. + ### Clients cannot connect Check that the host is listening on `80/tcp` and `443/tcp`, firewall rules allow inbound traffic, and backend IP addresses are reachable from Traefik. For services bound to host loopback, configure backends as `127.0.0.1:PORT`. diff --git a/docs/testing.md b/docs/testing.md index e8ba5e9..3206808 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -40,5 +40,6 @@ openssl s_client -connect LOAD_BALANCER_HOST:443 -servername www.azionelab.org - Docker/Compose validation: `docker compose config`. - Runtime health: `docker compose ps`. +- Backend health checks: Traefik load-balancer health checks configured per site. - HTTP route smoke test: `curl`. - TLS passthrough smoke test: `openssl s_client`.