Introduction to Traefik Proxy
Traefik is an open-source, cloud-native reverse proxy and load balancer designed to make deploying microservices easy and dynamic. Unlike traditional proxies that require manual reloads when configuration changes, Traefik automatically discovers services and updates its routing in real time. It integrates natively with orchestrators like Docker, Kubernetes, Swarm, and others, listening for changes and adjusting traffic routing without downtime.
Originally created by Containous (now Traefik Labs), Traefik has become one of the most popular ingress solutions in the container ecosystem. Its appeal lies in simplicity: you declare what you want, and Traefik figures out how to route traffic to it.
Why Traefik Matters
Traditional reverse proxies like Nginx or Apache HTTP Server were built for static infrastructure. When a new service spins up or scales out, you typically need to regenerate configuration files and reload the proxy. In dynamic, containerized environments where services appear and disappear constantly, this model becomes painful.
Traefik solves this by treating configuration as a living thing. It watches your orchestrator's API, detects new containers, and automatically creates routes. This means:
- Zero-downtime reconfiguration: No reloads required when services change.
- Automatic service discovery: Routes are created based on labels or annotations.
- Built-in Let's Encrypt support: Automatic TLS certificate provisioning and renewal.
- Observability: First-class support for metrics, tracing, and logging.
- Protocol support: HTTP, TCP, and UDP routing in a single tool.
Core Concepts
Before diving into configuration, it's important to understand Traefik's mental model. Traefik uses several key abstractions:
Providers
A provider is any system that tells Traefik what services exist. Docker, Kubernetes, Consul, and even static YAML files are all providers. Each provider exposes configuration that Traefik consumes to build its routing table.
Entrypoints
Entrypoints are the network ports Traefik listens on. For example, you might define an entrypoint on port 80 for HTTP traffic and another on port 443 for HTTPS. All incoming traffic enters through these ports.
Routers
A router inspects incoming requests and matches them against rules. Rules can match on hostnames, paths, headers, and more. When a request matches a rule, the router forwards it to a service.
Services
A service represents the actual backend that handles the request. It defines how to reach your application, including load balancing strategy across multiple instances.
Middlewares
Middlewares sit between routers and services, modifying requests or responses. They handle concerns like authentication, rate limiting, compression, retries, and header manipulation. Middlewares can be chained together for complex processing pipelines.
Installing Traefik
The most common way to run Traefik is as a Docker container. This keeps it isolated and easy to manage. Let's start with a basic setup using Docker Compose.
Basic Docker Compose Setup
Create a file named docker-compose.yml with the following content:
version: "3.8"
services:
traefik:
image: traefik:v3.0
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy
networks:
proxy:
external: false
Let's break down what's happening here:
--api.insecure=trueenables the dashboard on port 8080 without authentication. This is fine for local development but should never be used in production.--providers.docker=truetells Traefik to use Docker as a provider.--providers.docker.exposedbydefault=falsemeans containers must explicitly opt in to being routed by Traefik using labels.--entrypoints.web.address=:80defines an entrypoint namedweblistening on port 80.- The Docker socket is mounted so Traefik can inspect containers and read their labels.
Start Traefik with:
docker compose up -d
You can now visit http://localhost:8080 to see the Traefik dashboard. It will be empty because no services are registered yet.
Routing Your First Service
Let's add a simple web application and route traffic to it. We'll use the popular whoami image, which returns information about the incoming request.
Update your docker-compose.yml to include the whoami service:
version: "3.8"
services:
traefik:
image: traefik:v3.0
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy
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=80"
networks:
- proxy
networks:
proxy:
external: false
Restart the stack:
docker compose up -d
Now test the routing:
curl -H "Host: whoami.localhost" http://localhost
You should see a JSON response containing details about your request, including the hostname, remote address, and headers. Traefik successfully routed the request based on the Host header.
Understanding the Labels
The labels on the whoami service are how Docker provider configuration works. Let's examine each one:
traefik.enable=trueopts the container into Traefik routing, required because we setexposedbydefault=false.traefik.http.routers.whoami.rule=Host(`whoami.localhost`)creates a router namedwhoamithat matches requests to the hostwhoami.localhost.traefik.http.routers.whoami.entrypoints=webattaches this router to thewebentrypoint we defined earlier.traefik.http.services.whoami.loadbalancer.server.port=80tells Traefik that the service listens on port 80 inside the container.
Adding TLS with Let's Encrypt
One of Traefik's standout features is automatic HTTPS via Let's Encrypt. Let's extend our setup to support TLS certificates.
version: "3.8"
services:
traefik:
image: traefik:v3.0
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
networks:
- proxy
whoami:
image: traefik/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
- proxy
networks:
proxy:
external: false
Key additions in this configuration:
- A new
websecureentrypoint on port 443 for HTTPS traffic. - Automatic redirection from HTTP to HTTPS so all traffic is encrypted.
- A certificate resolver named
letsencryptusing the TLS-ALPN-01 challenge. - A persistent volume for storing certificates so they survive container restarts.
- The whoami router now uses the
websecureentrypoint and references the certificate resolver.
Replace you@example.com with your real email address and whoami.example.com with a domain that points to your server. Traefik will automatically obtain and renew certificates.
Using Middlewares
Middlewares are where Traefik gets powerful. Let's explore several common middleware patterns.
Rate Limiting
To protect your services from abuse, you can limit the number of requests per unit of time:
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.example.com`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.routers.api.middlewares=rate-limit"
- "traefik.http.middlewares.rate-limit.ratelimit.average=100"
- "traefik.http.middlewares.rate-limit.ratelimit.burst=50"
- "traefik.http.services.api.loadbalancer.server.port=8080"
This limits the service to an average of 100 requests per second with a burst capacity of 50.
Basic Authentication
You can add HTTP basic authentication to protect sensitive endpoints:
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`dashboard.example.com`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.middlewares=auth"
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$xyz123$$AbCdEfGhIjKlMnOpQrStU"
Generate the password hash using the htpasswd utility:
htpasswd -nb admin "your-secret-password"
Note that dollar signs in the hash must be doubled in Docker Compose files to avoid variable interpolation.
Chaining Multiple Middlewares
You can apply multiple middlewares by listing them comma-separated:
labels:
- "traefik.http.routers.app.middlewares=rate-limit,auth,compress"
- "traefik.http.middlewares.rate-limit.ratelimit.average=100"
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$xyz123$$hashvalue"
- "traefik.http.middlewares.compress.compress=true"
Requests will pass through rate limiting, then authentication, then compression, in that order.
Path-Based Routing
Sometimes you want to route different paths to different services under the same domain. Traefik supports this with path rules:
services:
frontend:
image: my-frontend
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`app.example.com`) && PathPrefix(`/`)"
- "traefik.http.routers.frontend.entrypoints=websecure"
- "traefik.http.routers.frontend.tls.certresolver=letsencrypt"
- "traefik.http.services.frontend.loadbalancer.server.port=80"
api:
image: my-api
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`app.example.com`) && PathPrefix(`/api`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.services.api.loadbalancer.server.port=3000"
Traefik evaluates routers in order of rule specificity. More specific rules should be defined to ensure correct matching. You can also assign priorities explicitly:
- "traefik.http.routers.api.priority=100"
- "traefik.http.routers.frontend.priority=1"
File-Based Configuration
While Docker labels are convenient, sometimes you need more control. Traefik supports a file provider for static configuration. This is useful for defining complex routers, middlewares, and services in a structured way.
Create a directory structure:
mkdir -p /etc/traefik/dynamic
Add a file /etc/traefik/dynamic/dynamic.yml:
http:
routers:
my-router:
rule: "Host(`example.com`)"
entrypoints:
- websecure
service: my-service
tls:
certResolver: letsencrypt
middlewares:
- rate-limit
middlewares:
rate-limit:
rateLimit:
average: 100
burst: 50
services:
my-service:
loadBalancer:
servers:
- url: "http://10.0.0.10:8080"
- url: "http://10.0.0.11:8080"
Enable the file provider in your Traefik configuration:
command:
- "--providers.file.directory=/etc/traefik/dynamic"
- "--providers.file.watch=true"
With watch=true, Traefik automatically reloads when files change, giving you dynamic configuration without restarts.
Load Balancing Strategies
Traefik provides several load balancing algorithms to distribute traffic across service instances.
Round Robin (Default)
By default, Traefik uses weighted round robin. This distributes requests evenly across all servers:
http:
services:
my-service:
loadBalancer:
servers:
- url: "http://server1:8080"
- url: "http://server2:8080"
Weighted Load Balancing
You can assign weights to control traffic distribution:
http:
services:
my-service:
weighted:
services:
- name: primary
weight: 9
- name: canary
weight: 1
This sends 90% of traffic to the primary service and 10% to a canary deployment, useful for gradual rollouts.
Sticky Sessions
For applications that require session affinity, enable sticky sessions:
http:
services:
my-service:
loadBalancer:
sticky:
cookie:
name: session_cookie
secure: true
httpOnly: true
servers:
- url: "http://server1:8080"
- url: "http://server2:8080"
Traefik sets a cookie on the first response, and subsequent requests with that cookie are routed to the same server.
Health Checks
Traefik can actively check the health of your backends and stop sending traffic to unhealthy instances:
http:
services:
my-service:
loadBalancer:
healthCheck:
path: /health
interval: 10s
timeout: 3s
servers:
- url: "http://server1:8080"
- url: "http://server2:8080"
If a server fails the health check, Traefik removes it from the rotation until it recovers.
Observability
Production systems need visibility. Traefik integrates with popular observability tools.
Access Logs
Enable access logs to record every request:
command:
- "--accesslog=true"
- "--accesslog.filepath=/var/log/traefik/access.log"
- "--accesslog.format=json"
Metrics with Prometheus
Expose metrics for Prometheus to scrape:
command:
- "--metrics.prometheus=true"
- "--metrics.prometheus.entrypoint=metrics"
- "--entrypoints.metrics.address=:8082"
Configure Prometheus to scrape http://your-traefik-host:8082/metrics.
Dashboard Security
In production, never expose the dashboard without authentication. Secure it properly:
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$hashvalue"
The api@internal service is a special built-in service that serves the dashboard.
Best Practices
Security
- Never expose the dashboard without authentication in production.
- Always redirect HTTP to HTTPS to enforce encryption.
- Use
exposedbydefault=falseto prevent accidental exposure of containers. - Restrict access to the Docker socket using a proxy like
docker-socket-proxyfor defense in depth. - Regularly update Traefik to get security patches.
Performance
- Enable response compression to reduce bandwidth usage.
- Use health checks to avoid routing to dead instances.
- Configure connection limits to protect backends from overload.
- Consider enabling HTTP/3 for reduced latency on supported clients.
Configuration Management
- Keep static configuration minimal and use dynamic configuration for service-specific settings.
- Version control your Traefik configuration files.
- Use a file provider for complex configurations that are hard to express in labels.
- Persist Let's Encrypt certificates on a volume to avoid hitting rate limits on restarts.
High Availability
For production deployments, run multiple Traefik instances behind a load balancer. If using Let's Encrypt, configure a shared storage backend (like Consul or Redis) so only one instance performs the ACME challenge at a time:
command:
- "--certificatesresolvers.letsencrypt.acme.storage=/acme/acme.json"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
Mount the same shared storage across all Traefik instances, or use the Consul provider for distributed locking.
TCP and UDP Routing
Traefik isn't limited to HTTP. It can also route TCP and UDP traffic, making it suitable for databases, message queues, and other non-HTTP services.
TCP Routing Example
tcp:
routers:
mysql-router:
entrypoints:
- mysql
rule: "HostSNI(`*`)"
service: mysql-service
services:
mysql-service:
loadBalancer:
servers:
- address: "10.0.0.20:3306"
Define the entrypoint in your static configuration:
command:
- "--entrypoints.mysql.address=:3306"
UDP Routing Example
udp:
routers:
dns-router:
entrypoints:
- dns
service: dns-service
services:
dns-service:
loadBalancer:
servers:
- address: "10.0.0.30:53"
Conclusion
Traefik has established itself as a first-class reverse proxy for modern, dynamic infrastructure. Its ability to automatically discover services, provision TLS certificates, and adapt to changing environments without reloads makes it an excellent choice for containerized applications. By understanding the core concepts of providers, entrypoints, routers, services, and middlewares, you can build robust routing configurations that scale with your needs. Whether you're running a simple homelab or a large production deployment, Traefik's combination of simplicity, flexibility, and powerful features makes it a compelling alternative to traditional reverse proxies. Start with the basic Docker Compose setup, add TLS and middlewares as needed, and follow the best practices outlined above to build a secure and observable proxy layer for your applications.