Traefik’s static and dynamic configuration is a bit like deciding between a fixed menu and an à la carte ordering system for your network traffic.

Let’s see this in action. Imagine you have a simple web service running on port 8000.

# Static Configuration (traefik.yml)
entryPoints:
  web:
    address: ":80"

providers:
  docker:
    exposedByDefault: false

api:
  dashboard: true

log:
  level: INFO

And your Docker service is labeled like this:

# Docker Compose (docker-compose.yml)
version: '3.8'
services:
  whoami:
    image: traefik/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
      - "traefik.http.routers.whoami.entrypoints=web"
      - "traefik.http.services.whoami.loadbalancer.server.port=8000"

When you start Traefik with traefik --configFile=traefik.yml and bring up your docker-compose up, hitting http://whoami.localhost in your browser will show you the whoami service. Traefik, acting as the reverse proxy, saw the Docker service and its labels, understood you wanted to route traffic for whoami.localhost to port 8000 on that container, and made it happen. The static config set up the entry point (:80) and told Traefik to look at Docker for dynamic routing rules, while the dynamic config (the labels) defined how to route.

The core problem Traefik solves is making complex routing logic manageable, especially in dynamic environments like Docker or Kubernetes. Instead of manually configuring your load balancer every time a service scales up or down, or a new one appears, Traefik can automatically discover and configure routes based on metadata.

Internally, Traefik has two main modes of configuration:

  • Static Configuration: This is the "set it and forget it" part. It defines the core operational parameters of Traefik itself: which network ports it listens on (entry points), how it should log, whether to enable the dashboard, and crucially, which providers to use to discover dynamic configuration. This is typically set via a traefik.yml or traefik.toml file, or command-line flags. You don’t change this often.

  • Dynamic Configuration: This is where the magic happens for routing traffic. Traefik watches various "providers" (like Docker, Kubernetes, Consul, etcd, or even plain files) for changes. When a provider detects a new service or an update to an existing one, it signals Traefik, which then dynamically updates its routing table without restarting. For Docker, this often means reading container labels; for Kubernetes, it means watching Ingress resources or Custom Resource Definitions.

The interplay between static and dynamic configuration is key. The static config tells Traefik where to listen and what systems to watch for routing instructions. The dynamic config (from labels, Ingresses, etc.) tells Traefik what traffic to accept and where to send it.

You can also configure Traefik using TOML files. Here’s the equivalent static config:

# Static Configuration (traefik.toml)
[entryPoints]
  [entryPoints.web]
    address = ":80"

[providers]
  [providers.docker]
    exposedByDefault = false

[api]
  dashboard = true

[log]
  level = "INFO"

The concept of "routers" and "services" is fundamental to Traefik’s dynamic configuration. A router matches incoming requests based on rules (like hostnames, paths, headers, methods) and entry points. Once a request matches a router, Traefik forwards it to a service. A service defines how to reach your actual application instances, often involving load balancing across multiple backend servers. Labels in Docker or annotations in Kubernetes are essentially defining these routers and services.

Traefik’s static configuration can also specify default routers and services, which apply if no dynamic configuration is found for a request. This is less common for complex setups but can be useful for simple, static routing needs.

When using file providers for dynamic configuration (e.g., a dynamic_conf.yml file), Traefik will continuously watch this file for changes. This is useful for managing routes without relying on external systems like Docker or Kubernetes, or for overriding provider-specific configurations.

# dynamic_conf.yml
http:
  routers:
    my-router:
      rule: "Host(`example.com`)"
      service: "my-service"
      entryPoints:
        - "web"
  services:
    my-service:
      loadBalancer:
        servers:
          - url: "http://192.168.1.100:8080"

The most surprising thing about Traefik’s configuration is how easily you can switch between different providers or even combine them. For instance, you might use Docker labels for your containerized applications but a file provider for a legacy application running on a specific IP. Traefik’s static configuration is the conductor, telling it which instruments (providers) to listen to, and it seamlessly integrates their outputs into a unified routing table.

The next hurdle you’ll likely encounter is understanding how to manage TLS certificates with Traefik, especially when dealing with multiple domains.

Want structured learning?

Take the full Traefik course →