Traefik doesn’t just route traffic; it discovers your services and configures itself on the fly, eliminating the need for manual reloads and simplifying complex routing rules.
Let’s see Traefik in action with a simple setup. Imagine you have two identical web services, webapp-a and webapp-b, both running on port 80. We want Traefik to distribute incoming requests to these services evenly.
First, we’ll define our services using Docker labels. Traefik will watch the Docker socket for these labels and automatically create routes.
# For webapp-a
FROM nginx:alpine
LABEL traefik.enable=true
LABEL traefik.http.routers.webapp-a.rule="Host(`my-app.example.com`)"
LABEL traefik.http.routers.webapp-a.service="webapp-a"
LABEL traefik.http.services.webapp-a.loadbalancer.server.port=80
# For webapp-b
FROM nginx:alpine
LABEL traefik.enable=true
LABEL traefik.http.routers.webapp-b.rule="Host(`my-app.example.com`)"
LABEL traefik.http.routers.webapp-b.service="webapp-b"
LABEL traefik.http.services.webapp-b.loadbalancer.server.port=80
Now, let’s configure Traefik itself. We’ll use a traefik.yml file:
api:
dashboard: true
insecure: true # For simplicity in this example, enable dashboard without auth
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false # Only services with traefik.enable=true will be discovered
entryPoints:
web:
address: ":80"
And run Traefik in a container:
docker run -d \
-p 80:80 \
-p 8080:8080 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ./traefik.yml:/etc/traefik/traefik.yml \
traefik:v2.9
With this setup, when Traefik starts, it connects to the Docker daemon. It discovers the webapp-a and webapp-b services because they have traefik.enable=true. It sees that both services are configured to respond to requests for Host(\my-app.example.com`). Traefik automatically creates a router named webapp-aand another namedwebapp-b, both pointing to the web` entrypoint. For each router, it defines a backend service. Since we haven’t specified any load balancing strategy, Traefik defaults to round-robin.
Now, if you send a request to my-app.example.com (you’d need to set up DNS or /etc/hosts for this), Traefik will forward it to either webapp-a or webapp-b. If you hit refresh repeatedly, you’ll see the requests alternating between the two services. You can verify this by adding a simple <h1>Hello from Service X</h1> to each nginx container and observing the output. The Traefik dashboard (accessible at http://localhost:8080) will show the defined routers and services, confirming that Traefik has dynamically configured itself.
The core problem Traefik solves is the decoupling of service discovery from routing configuration. Traditionally, when you add or remove a service, or change its port, you’d have to manually update a load balancer configuration file (like Nginx or HAProxy) and then reload the service. Traefik, by integrating with orchestrators like Docker or Kubernetes, watches for changes. When a new container with Traefik labels appears, Traefik immediately configures a route for it. When a container disappears, Traefik removes the route. This dynamic re-configuration is the magic.
The routing rules, specified with traefik.http.routers.<router_name>.rule, are powerful. They can be based on host, path, headers, methods, and even combinations of these using logical operators (&&, ||, !). For example, Host(\api.example.com`) && PathPrefix(`/users`)would only match requests toapi.example.com/users` and any subpaths.
The service definition, like traefik.http.services.<service_name>.loadbalancer.server.port=80, tells Traefik which port on the container to send traffic to. If you have multiple instances of the same service running, Traefik will automatically discover them and add them as servers to the defined load balancer service.
When you configure multiple routers to point to the same service (e.g., webapp-a and webapp-b both using the webapp service), Traefik’s load balancing strategy comes into play. The default is roundRobin. Other strategies include leastConnection (sends traffic to the server with the fewest active connections) and random (chooses a server randomly). You can specify these in your Docker labels: LABEL traefik.http.services.webapp.loadbalancer.strategy=leastConnection. This is configured by Traefik by creating internal backend services that are composed of the discovered container IPs and ports, and then dynamically updating the routing table based on these services and the defined routers.
What many people miss is that Traefik’s dynamic configuration isn’t just about adding new routes. It’s also about modifying existing ones and removing them. If you change a Host rule on a running container, Traefik will update the router’s rule instantly. If you stop a container, Traefik will remove its associated service endpoints and update the router to no longer send traffic to that dead instance. This constant reconciliation is what makes it so robust.
The next step in understanding Traefik is exploring its middleware capabilities, which allow you to chain various request processing functions like authentication, rate limiting, and request/response modification before traffic even reaches your services.