In Docker Compose, Traefik finds the containers to route through the Docker socket, mounted with the line /var/run/docker.sock:/var/run/docker.sock, and each service opts in with traefik.* labels. The static configuration goes in /etc/traefik/traefik.yml.
The docker run command, converted
The v3 command from the image’s Docker Hub page, with the tag pinned to the current minor release:
docker run -d -p 8080:8080 -p 80:80 \
-v $PWD/traefik.yml:/etc/traefik/traefik.yml \
-v /var/run/docker.sock:/var/run/docker.sock \
traefik:v3.7
services:
traefik:
image: traefik:v3.7
ports:
- "8080:8080"
- "80:80"
volumes:
- $PWD/traefik.yml:/etc/traefik/traefik.yml
- /var/run/docker.sock:/var/run/docker.sock
The service is named traefik after the image, since the command has no --name. Compose fills in $PWD from the environment of whoever runs it, which is the current directory, not the file’s. Write ./traefik.yml, which Compose resolves next to the file.
A routed service
# traefik.yml
entryPoints:
web:
address: ":80"
providers:
docker:
exposedByDefault: false
api:
insecure: true
ping: {}
services:
traefik:
image: traefik:v3.7
ports:
- "80:80"
- "127.0.0.1:8080:8080"
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
healthcheck:
test: ["CMD", "traefik", "healthcheck", "--ping"]
interval: 10s
timeout: 3s
retries: 3
whoami:
image: traefik/whoami:v1.12
labels:
traefik.enable: "true"
traefik.http.routers.whoami.rule: Host(`whoami.docker.localhost`)
depends_on:
traefik:
condition: service_healthy
traefik healthcheck --ping calls the /ping endpoint, which is off until the static configuration has ping. curl -H 'Host: whoami.docker.localhost' http://127.0.0.1/ then reaches whoami.
Gotchas
- If
traefik.ymldoesn’t exist when the container starts, Docker creates a directory by that name, and Traefik stops withread /etc/traefik/traefik.yml: is a directory. exposedByDefaultis true unless you turn it off, and then Traefik makes a router for every container the Docker daemon runs, other projects’ included.- The Docker Hub page’s
defaultRulebuilds the host from the container’s.Name. Under Compose that name is<service>-<project>, so itstest.docker.localhostbecomestest-myproject.docker.localhost. :roon the socket doesn’t make the Docker API read-only: connecting to a socket isn’t a write to the file, so Traefik, or anything that takes it over, can still start and stop containers. A socket proxy that allows only reads is the stricter setup.api.insecure: trueserves the dashboard on 8080 with no login, so publish it on127.0.0.1only.