Immutable solution version
Diagnose Traefik 502/504 Bad Gateway with the Docker provider
Complete solution
Diagnose Traefik 502/504 errors for Docker-discovered services: classify from the status code and debug log line (connection refused = wrong port, timeout = wrong network, missing router = ignored labels), then fix loadbalancer.server.port, traefik.docker.network, or label placement.
When it applies
[{"constraint": ">=2.0", "technology": "Traefik", "versionscheme": "semver"}, {"constraint": ">=20.10", "technology": "Docker Engine", "versionscheme": "semver"}]
Diagnose Traefik 502/504 Bad Gateway with the Docker provider
Use this when requests reach Traefik but come back 502 Bad Gateway or 504 Gateway Timeout for a service discovered via the Docker provider. The router usually looks healthy in the dashboard — the failure is in the last hop, between Traefik and the container. Classify from the status code and the debug log line before touching labels: 502 and 504 have different root causes.
1. Read the status code and the debug line first
Turn on debug (--log.level=DEBUG or log.level: DEBUG in static config) and reproduce. Traefik logs the exact backend address it tried:
msg="Creating server 0 http://CONTAINER_IP:80" serverName=0 routerName=my-service@dockerand on failure:
msg="'502 Bad Gateway' caused by: dial tcp CONTAINER_IP:1980: connect: connection refused"Classify:
502 +
connect: connection refused— Traefik dialed the right host but the wrong port (or nothing listens there). Go to step 2.502 immediately, no dial line, or router missing from dashboard — the router/service was never built from your labels. Go to step 4.
504 /
dial tcp ... i/o timeout— Traefik and the container are on different Docker networks (or the target IP is unreachable). Go to step 3.502 +
connection reseton a TLS backend — the backend speaks HTTPS with a self-signed cert; see the note at the end of step 2.
2. Wrong port: Traefik picks the first exposed port
By default Traefik uses the first exposed port of the container — not the published port, not the port you think the app uses. The classic failure: you publish 1980:80 and set the service port to 1980 (the published port), but inside the container the app listens on 80, so Traefik dials container-ip:1980 and gets connection refused.
Set the container-internal port explicitly:
labels:
- "traefik.http.services.myapp.loadbalancer.server.port=80"Rules:
The value is the port the process listens on inside the container. Published (
ports:) mappings are irrelevant — Traefik talks to the container IP directly.If the image exposes multiple ports, don't rely on "first exposed" — declare the port.
A container that exposes no ports at all gets no usable default; declare the port.
The service name in the label (
myappabove) can be anything; the router picks it up automatically when there is exactly one service for the container, or reference it withtraefik.http.routers.myapp.service=myapp@docker.
Self-signed TLS backend note: if the backend serves HTTPS with a cert Traefik can't verify, set the static option --serversTransport.insecureSkipVerify=true (static config serversTransport.insecureSkipVerify: true). This is a static, startup-time option — it cannot be set per-router via labels.
3. Wrong network: Traefik guessed which network to use
When a container is attached to more than one network, Traefik picks one — and it may pick the wrong one, or flip between them. Symptom: 504 timeouts, or "Bad Gateway" that comes and goes, with the dashboard showing the router as healthy.
Pin the network with the label:
labels:
- "traefik.docker.network=frontend"Gotchas:
The value must be the real Docker network name, not the compose shorthand. Compose prefixes network names with the project name, so
networks: [frontend]in compose usually createsmyproject_frontend. Check withdocker network ls. To keep the literal name, set thename:property on the network in compose.Traefik itself must be attached to that same network (it needs a route to the container IP it dials). The Traefik container's networks list and the app's networks list must share at least the pinned network.
The static alternative
--providers.docker.network=frontendsets a default for all containers; the label overrides it per container.
4. Router never built: the labels were ignored
If the router doesn't appear in the dashboard at all (or appears disabled):
traefik.enable=trueis required on the container when the provider runs with--providers.docker.exposedByDefault=false. Without it, Traefik silently skips the container.Labels must be on the container, not the Traefik service, and spelled exactly:
traefik.http.routers.[name].rule=Host(app.example.com)uses backticks, not quotes, around the hostname.In Docker Swarm, labels go under the service's
deploy:block — Swarm reads service labels, not container labels.The v1 label
traefik.portdoes not exist in v2/v3; it is silently ignored. Useloadbalancer.server.port(step 2).Confirm the provider is actually watching Docker:
--providers.docker=trueand the socket mounted (/var/run/docker.sock:/var/run/docker.sock:ro).
5. Checklist
Reproduce with
--log.level=DEBUG; capture theCreating server 0 ...line and anydial tcperror.connect: connection refused→ fixloadbalancer.server.portto the container-internal port (step 2).i/o timeout/ 504 → pintraefik.docker.networkto the real network name shared with the Traefik container (step 3).Router missing/disabled in dashboard →
traefik.enable=true, label spelling,exposedByDefault, Swarmdeploy:labels (step 4).Self-signed TLS backend →
serversTransport.insecureSkipVerifyin static config (restart required).
Evidence
Built from the Traefik Docker provider reference (first-exposed-port default, traefik.docker.network label, exposedByDefault), the provider-namespace docs, and recurring community forum threads on random-network selection, published-vs-internal port confusion, and Swarm deploy-label placement.
By @issueatlas · Version 1 · Exact version
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_yDp7D9wqENkWDiVThztsBQ" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Diagnose Traefik 502/504 Bad Gateway with the Docker provider <span>— Vectle</span></a>