fix(phasefinal-web): healthcheck targeted ::1, so traefik skipped the container
The healthcheck used http://localhost/, which resolves to ::1 in nginx:alpine while nginx listens on IPv4 only — so it never passed, the container stayed unhealthy, and Traefik silently declined to create a router for it. That presents as a broken docker provider: correct labels, right network, no route, no error. Target 127.0.0.1 explicitly and add a start_period. Adds the apex router (301 phasefinal.com -> www) and drops the file-provider workaround, which was mitigating the wrong diagnosis.
This commit is contained in:
@@ -21,14 +21,20 @@ requests rule. Both must be undone on import:
|
||||
Without these the page silently falls back to system fonts and looks wrong
|
||||
rather than broken, which is the failure mode you won't notice.
|
||||
|
||||
## Routing
|
||||
## Routing — and the healthcheck trap that looks like a routing bug
|
||||
|
||||
`conf/traefik-dynamic-phasefinal-web.yml` deploys to
|
||||
**`/opt/docker/conf/traefik-ana/config/phasefinal-web.yml`** — not into this
|
||||
stack's own conf dir. It exists because Traefik's docker provider on ana-docker
|
||||
was not registering newly-created containers on 2026-08-29; the compose file
|
||||
still carries the correct `traefik.*` labels, so once that is fixed the file
|
||||
router can be deleted. See the header comment in that file.
|
||||
Routers come from the compose labels: `phasefinal-web` serves
|
||||
`www.phasefinal.com`; `phasefinal-apex` 301s `phasefinal.com` to it.
|
||||
|
||||
⚠ **Traefik silently skips containers Docker reports as unhealthy.** There is no
|
||||
error, no log line, and no router — it looks exactly like a broken provider. The
|
||||
first deploy of this stack hit that: the healthcheck used
|
||||
`http://localhost/`, which resolves to `::1` in `nginx:alpine`, and nginx listens
|
||||
on IPv4 only, so the check got "Connection refused" forever and the container
|
||||
never left `unhealthy`. **Use `127.0.0.1`, never `localhost`, in a healthcheck
|
||||
for an IPv4-only listener** — and if a correctly-labelled container never appears
|
||||
in `/api/http/routers`, check `docker inspect --format '{{.State.Health.Status}}'`
|
||||
*before* suspecting Traefik.
|
||||
|
||||
## Deploy
|
||||
|
||||
|
||||
@@ -9,16 +9,30 @@ services:
|
||||
networks:
|
||||
- tnet
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost/"]
|
||||
# 127.0.0.1, NOT localhost: in this image `localhost` resolves to ::1 and
|
||||
# nginx listens on IPv4 only, so `localhost` gives "Connection refused"
|
||||
# forever. Traefik SKIPS unhealthy containers, so a wrong healthcheck here
|
||||
# silently means "no route" rather than "unhealthy service".
|
||||
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:80/"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.phasefinal-web.rule=Host(`www.phasefinal.com`)
|
||||
- traefik.http.routers.phasefinal-web.tls=true
|
||||
- traefik.http.routers.phasefinal-web.tls.certresolver=anaprod
|
||||
- traefik.http.services.phasefinal-web.loadbalancer.server.port=80
|
||||
# apex -> www, 301
|
||||
- traefik.http.routers.phasefinal-apex.rule=Host(`phasefinal.com`)
|
||||
- traefik.http.routers.phasefinal-apex.tls=true
|
||||
- traefik.http.routers.phasefinal-apex.tls.certresolver=anaprod
|
||||
- traefik.http.routers.phasefinal-apex.service=phasefinal-web
|
||||
- traefik.http.routers.phasefinal-apex.middlewares=phasefinal-to-www
|
||||
- traefik.http.middlewares.phasefinal-to-www.redirectregex.regex=^https?://phasefinal\.com/(.*)
|
||||
- traefik.http.middlewares.phasefinal-to-www.redirectregex.replacement=https://www.phasefinal.com/$${1}
|
||||
- traefik.http.middlewares.phasefinal-to-www.redirectregex.permanent=true
|
||||
|
||||
networks:
|
||||
tnet:
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# Traefik file-provider router for phasefinal-web.
|
||||
#
|
||||
# DEPLOYS TO: ana-docker:/opt/docker/conf/traefik-ana/config/phasefinal-web.yml
|
||||
# (NOT /opt/docker/conf/phasefinal-web/ — the file provider watches
|
||||
# traefik-ana/config, so this file lives with Traefik, not the stack.)
|
||||
#
|
||||
# WHY A FILE ROUTER instead of container labels: as of 2026-08-29 the Traefik
|
||||
# docker provider on ana-docker was not registering newly-created containers —
|
||||
# phasefinal-web carried correct traefik.* labels, sat on traefik-net, and never
|
||||
# appeared in /api/http/routers, with no provider activity in the logs. The file
|
||||
# provider (watch: true) picks this up with no Traefik restart, so no other
|
||||
# public service is interrupted. If the docker provider is fixed, this file can
|
||||
# be deleted and the compose labels will take over.
|
||||
http:
|
||||
routers:
|
||||
phasefinal-web:
|
||||
rule: "Host(`www.phasefinal.com`)"
|
||||
service: phasefinal-web
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: anaprod
|
||||
services:
|
||||
phasefinal-web:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://phasefinal-web:80"
|
||||
Reference in New Issue
Block a user