Traefik, when used with Docker Compose, can feel like magic because it automatically discovers and routes traffic to your services without you needing to touch any load balancer configuration files.

Let’s see this in action. Imagine you have a simple web service, whoami, that just tells you what IP address it sees.

Here’s your docker-compose.yml:

version: '3.8'

services:
  traefik:
    image: traefik:v2.9
    command:
      - --api.insecure=true # For demonstration purposes only!
      - --providers.docker=true
      - --entrypoints.web.address=:80
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    labels:
      - "traefik.enable=true" # Crucial for Traefik to manage this service
      - "traefik.http.routers.traefik.rule=Host(`traefik.localhost`)" # Route traffic for Traefik UI
      - "traefik.http.routers.traefik.entrypoints=web"
      - "traefik.http.services.traefik.loadbalancer.server.port=8080" # Traefik's API port

  whoami:
    image: traefik/whoami
    labels:
      - "traefik.enable=true" # Traefik will manage this service
      - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)" # Route traffic for the whoami service
      - "traefik.http.routers.whoami.entrypoints=web"
      - "traefik.http.services.whoami.loadbalancer.server.port=80" # The port the whoami service listens on

When you run docker compose up, Traefik starts, and because of the traefik.enable=true label on the whoami service, Traefik automatically creates a route for it. If you then access http://whoami.localhost in your browser (you might need to add this to your /etc/hosts file mapping to 127.0.0.1), you’ll see the output from the whoami container. You can also access the Traefik dashboard at http://traefik.localhost.

The problem Traefik solves is dynamic service discovery and routing. In a traditional setup, you’d have a load balancer (like Nginx or HAProxy) and you’d have to manually edit its configuration file every time you start, stop, or update a service, then reload the balancer. With Traefik and Docker labels, this is all automated. Traefik watches the Docker socket for container events. When a container with traefik.enable=true appears, it inspects its labels to know how to route traffic to it.

Internally, Traefik uses a provider model. The providers.docker=true command-line argument tells Traefik to enable the Docker provider. This provider connects to the Docker API (via the mounted docker.sock) and listens for events like container creation, deletion, and label changes. When an event occurs, Traefik re-evaluates its routing configuration.

The key levers you control are the labels on your services:

  • traefik.enable=true: This is the on/off switch. If this isn’t present or set to false, Traefik ignores the service.
  • traefik.http.routers.<router-name>.rule: This defines when traffic should be routed to this service. Host(\service.localhost`)is the most common, matching based on the incomingHostheader. You can also usePathPrefix, Method, Header, and combinations with &&(AND) and||` (OR).
  • traefik.http.routers.<router-name>.entrypoints: This specifies which of Traefik’s defined entrypoints (like web for port 80, websecure for 443) this router should listen on.
  • traefik.http.services.<service-name>.loadbalancer.server.port: This tells Traefik which port inside the container it should send traffic to. Traefik will automatically discover the container’s IP address.

A common pattern is to define default configurations for routers and services that all your containers can inherit, and then override specific settings with labels. For example, you could have a base Traefik configuration that sets up TLS for websecure entrypoints, and then your service labels would only need to specify the Host rule and enable Traefik.

When Traefik processes the traefik.http.routers.whoami.rule=Host(\whoami.localhost`)andtraefik.http.services.whoami.loadbalancer.server.port=80, it creates an internal representation. It finds the whoamicontainer, gets its IP address (e.g.,172.18.0.3), and configures itself to send any incoming HTTP request with the Hostheaderwhoami.localhostto that IP address on port80. The traefik.http.services.traefik.loadbalancer.server.port=8080label for the Traefik service itself is important because Traefik's API and dashboard typically run on port8080within its container, not the standard web port80`.

The most surprising thing is how Traefik handles health checks automatically if you enable them. By default, if you don’t specify a health check, Traefik might try to send traffic to a container that’s still starting up. However, if you add traefik.http.services.whoami.loadbalancer.healthcheck.path=/ to your labels, Traefik will periodically ping the / path on your service. If the health check fails, Traefik will temporarily stop sending traffic to that instance until it becomes healthy again. This is managed entirely through labels, without needing to configure health checks in Docker itself.

The next concept to explore is how to manage TLS certificates for HTTPS automatically using Let’s Encrypt.

Want structured learning?

Take the full Traefik course →