Skip to content

Traefik, and four starter bugs

The infra repo’s starter stack provides Traefik, a docker-socket-proxy, Portainer and a whoami placeholder. Every one of the bugs below passed docker compose config — they only appeared when the stack ran, or when the config was loaded into the real Traefik version.

Traefik reads container labels from the Docker API. Mounting /var/run/docker.sock — even :ro — gives it full control of the daemon: :ro only stops the socket file being replaced, not API writes. Traefik’s own docs warn this can hand an attacker the host. tecnativa/docker-socket-proxy exposes only read-only listing (CONTAINERS=1, NETWORKS=1, POST=0), and Traefik talks to tcp://docker-socket-proxy:2375.

Validate the dynamic config before deploying it

Section titled “Validate the dynamic config before deploying it”
Terminal window
docker run --rm -d --name tf-test -v "$PWD/etc/traefik:/dyn:ro" traefik:v3.7.17 \
--providers.file.filename=/dyn/dynamic.yml --api.insecure=true
docker logs tf-test | grep ERR
docker exec tf-test wget -qO- http://127.0.0.1:8080/api/http/middlewares
docker rm -f tf-test

Bug 1 — the whole middleware file was rejected

Section titled “Bug 1 — the whole middleware file was rejected”
ERR Error while building configuration … error="field not found, node: passiveHealthCheck"

passiveHealthCheck was inside the rate-limit middleware. In Traefik it is a service load-balancer option. One bad field drops the entire file, so rate-limit@file and secure-headers@file never existed — and every router that referenced them failed. Set it per service if you want it: traefik.http.services.<svc>.loadbalancer.passiveHealthCheck.failureWindow=30s.

Behind Cloudflare the connecting IP is a Cloudflare edge, so per-IP limits are shared by every visitor on that edge. Group by the real visitor instead:

rate-limit:
rateLimit:
average: 100
burst: 200
period: 1s
sourceCriterion:
requestHeaderName: CF-Connecting-IP

Bug 3 — secure-headers told Google not to index the site

Section titled “Bug 3 — secure-headers told Google not to index the site”

The shared secure-headers middleware also sent X-Robots-Tag: "noindex, nofollow". Every public site used it. That header moved into its own noindex@file middleware, for admin tools only.

Symptom Cause Fix
socket-proxy crash-loops: unknown optional log level 'warn' Its HAProxy 3.4 (image v0.5.0) dropped warn LOG_LEVEL=warning
field not found, node: strategy; routers never load, no certificate requested loadbalancer.server.strategy isn’t a field traefik.http.services.<svc>.loadbalancer.strategy=leasttime
Traefik “unhealthy”: please enable ping --ping never set, and traefik healthcheck reads its own flags --ping=true and test: ["CMD", "traefik", "healthcheck", "--ping"]

make up in the starter also pulled in the monitoring and backup files, and the core file includes Portainer — whose first visitor creates the admin account. Start services by name instead:

Terminal window
docker network create traefik_public
docker compose -f compose.yml up -d docker-socket-proxy traefik whoami

Pin image versions — after checking them

Section titled “Pin image versions — after checking them”

Every image the starter pinned was out of date, and two pins were wrong outright:

  • henrygd/beszel-hub does not exist on Docker Hub; the hub image is henrygd/beszel.
  • portainer/agent’s newest tag is a new major (3.x); keep it on the same 2.x line as the Portainer server.

Check the newest stable tag, then that it exists for your CPU:

Terminal window
docker manifest inspect traefik:v3.7.17 | grep -q amd64 && echo ok

(Docker Hub rate-limits anonymous pulls and manifest lookups; space them out or log in.)