Traefik Labels: A Practical Reference
Traefik configuration via Docker labels, demystified — routers, services, middlewares, and TLS resolvers with copy-paste examples.
Traefik’s label-based configuration is powerful but terse. The official docs are comprehensive; this post is the cheat sheet I wish I’d had when I started.
The Mental Model
Three concepts:
- Routers match incoming requests (by Host, Path, etc.) and route them.
- Services forward traffic to a backend container’s port.
- Middlewares transform requests/responses on the way through.
Every label is namespaced under one of these. The pattern is:
traefik.<http>.<routers|services|middlewares>.<name>.<option>=<value>
A Minimal HTTPS Service
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.myapp.rule=Host(`app.example.com`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=le"
- "traefik.http.services.myapp.loadbalancer.server.port=8080"
networks:
traefik:
external: true
That’s the irreducible HTTPS service. Six lines of labels, one network, one port. Don’t add anything else until you need it.
Entrypoints and Cert Resolvers
websecure is conventionally the HTTPS entrypoint (port 443). le is
typically a Let’s Encrypt ACME resolver defined in Traefik’s static config.
If you’re using a self-signed cert for development, drop the certresolver
line and add tls: true to the router instead.
Middlewares: Compose, Don’t Duplicate
Middlewares are reusable. Define a CORS middleware once and reference it from many routers:
- "traefik.http.middlewares.cors.headers.accessControlAllowOriginList=https://app.example.com"
- "traefik.http.middlewares.cors.headers.accessControlAllowMethods=GET,POST,PUT"
- "traefik.http.middlewares.cors.headers.addVaryHeader=true"
Attach it with a comma-separated list:
- "traefik.http.routers.myapp.middlewares=cors@docker"
The @docker suffix disambiguates the source provider. It’s optional in
most setups but makes logs easier to read.
Common Pitfalls
Forgetting the network. Traefik can only route to containers on a network
it shares. The traefik.docker.network label tells Traefik which network to
use when multiple are attached.
Priority confusion. When two routers could match the same request
(overlapping Host rules), Traefik sorts by rule length by default. Use an
explicit priority label when you need to override that.
Internal-only services. Set traefik.docker.network carefully and use
traefik.enable=false on services you don’t want exposed at all — it’s
safer than relying on network isolation.
Verifying Your Config
docker logs traefik will tell you which routers it discovered. The Traefik
dashboard (if you’ve enabled it) shows the full configuration tree. When
labels aren’t working, the answer is almost always in one of those two
places.
That’s the workflow: label the container, attach the network, watch the logs. Everything else is middlewares.