Traefik Reverse Proxy with Docker
Traefik is a modern reverse proxy and load balancer designed specifically for containerized environments, offering automatic service discovery, dynamic configuration, and Let's Encrypt SSL/TLS support. This comprehensive guide covers Docker provider integration, routing configuration with labels, service discovery, SSL/TLS certificate management, middleware configuration, and advanced features. Traefik eliminates manual proxy configuration and scales seamlessly with your containerized infrastructure.
Table of Contents
- Understanding Traefik
- Installing and Configuring Traefik
- Docker Provider Setup
- Service Routing Configuration
- SSL/TLS with Let's Encrypt
- Load Balancing Strategies
- Middleware Configuration
- Dashboard and Monitoring
- Advanced Features
- Conclusion
Understanding Traefik
Traefik automatically discovers services in your Docker environment and configures reverse proxying without requiring manual configuration updates. It watches for Docker events and dynamically adjusts routing based on container labels.
Key benefits:
- Automatic service discovery from Docker
- Dynamic configuration without restarts
- Built-in Let's Encrypt support for SSL/TLS
- Web dashboard for monitoring and debugging
- Middleware for request/response modification
- Load balancing across container instances
- API-driven configuration
- Multi-protocol support (HTTP, TCP, UDP)
Architecture components:
- Provider: Discovers services (Docker provider)
- Entrypoints: Network entry points (HTTP, HTTPS, etc.)
- Routers: Define routing rules and services
- Services: Backends receiving traffic
- Middleware: Transform requests/responses
Installing and Configuring Traefik
Deploy Traefik as a containerized service.
Basic Traefik setup:
# Create Traefik configuration directory
mkdir -p /opt/traefik
cd /opt/traefik
# Create traefik.yml configuration
cat > traefik.yml <<'EOF'
# API and dashboard
api:
insecure: false
dashboard: true
# Entrypoints
entryPoints:
http:
address: ":80"
https:
address: ":443"
# Docker provider
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik
# Logging
log:
level: INFO
# Access logs
accessLog:
filePath: /logs/access.log
# Metrics
metrics:
prometheus: {}
EOF
# Create docker-compose for Traefik
cat > docker-compose.yml <<'EOF'
version: '3.9'
services:
traefik:
image: traefik:v3.0
container_name: traefik
command:
- "--configFile=/traefik.yml"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik.yml:/traefik.yml
- ./acme.json:/acme.json
- ./logs:/logs
networks:
- traefik
restart: always
networks:
traefik:
driver: bridge
EOF
# Create empty acme.json for Let's Encrypt
touch acme.json
chmod 600 acme.json
mkdir -p logs
# Start Traefik
docker-compose up -d
# Verify running
docker-compose ps
Traefik configuration with static config file:
cat > traefik-static.yml <<'EOF'
# Traefik Static Configuration
# API and Dashboard
api:
insecure: false
dashboard: true
debug: false
# Entrypoints
entryPoints:
http:
address: ":80"
http:
redirectEntryPoint: https
https:
address: ":443"
http:
tls:
certResolver: letsencrypt
# Providers
providers:
docker:
endpoint: unix:///var/run/docker.sock
exposedByDefault: false
network: traefik
file:
filename: /etc/traefik/dynamic.yml
watch: true
# Certificate resolution
certificateResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /acme.json
httpChallenge:
entryPoint: http
# Logging
log:
level: INFO
filePath: /logs/traefik.log
# Access logs
accessLog:
filePath: /logs/access.log
format: json
# Metrics
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
EOF
# Use in container
docker run -d \
--name traefik \
-p 80:80 \
-p 443:443 \
-p 8080:8080 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ./traefik-static.yml:/etc/traefik/traefik.yml \
-v ./acme.json:/acme.json \
-v ./logs:/logs \
traefik:v3.0
Docker Provider Setup
Configure Traefik to automatically discover Docker services.
Docker provider configuration:
# In traefik.yml
cat >> traefik.yml <<'EOF'
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: "traefik"
swarmMode: false
useBindPortIP: false
EOF
# Restart Traefik
docker-compose restart traefik
# Verify Docker provider connected
docker logs traefik | grep -i docker
Network configuration for service discovery:
# Docker Compose with Traefik network
cat > docker-compose.yml <<'EOF'
version: '3.9'
services:
traefik:
image: traefik:v3.0
networks:
- traefik
volumes:
- /var/run/docker.sock:/var/run/docker.sock
web:
image: nginx:alpine
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(\`example.com\`)"
- "traefik.http.services.web.loadbalancer.server.port=80"
networks:
traefik:
driver: bridge
EOF
# Services must be on same network as Traefik for discovery
docker-compose up -d
Service Routing Configuration
Configure routing using Docker labels.
Basic routing with labels:
# Deploy service with routing labels
docker run -d \
--name myapp \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.myapp.rule=Host(\`app.example.com\`)" \
--label "traefik.http.routers.myapp.entrypoints=http,https" \
--label "traefik.http.services.myapp.loadbalancer.server.port=5000" \
myapp:latest
# Access via Traefik
curl http://app.example.com
Multiple routes for single service:
docker run -d \
--name api \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.api.rule=Host(\`api.example.com\`)" \
--label "traefik.http.routers.api-path.rule=Host(\`example.com\`) && PathPrefix(\`/api\`)" \
--label "traefik.http.services.api.loadbalancer.server.port=8080" \
myapi:latest
Path-based routing:
# Route by path prefix
docker run -d \
--name web \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.web.rule=PathPrefix(\`/\`)" \
--label "traefik.http.services.web.loadbalancer.server.port=80" \
nginx:latest
docker run -d \
--name app \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.app.rule=PathPrefix(\`/app\`)" \
--label "traefik.http.services.app.loadbalancer.server.port=5000" \
myapp:latest
# Traffic to /app/* goes to app service
# Other traffic goes to web service
Method-based routing:
# Route by HTTP method
docker run -d \
--name api \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.api-get.rule=Method(\`GET\`)" \
--label "traefik.http.routers.api-post.rule=Method(\`POST\`)" \
--label "traefik.http.services.api.loadbalancer.server.port=8080" \
api:latest
SSL/TLS with Let's Encrypt
Configure automatic SSL/TLS certificate management.
Let's Encrypt configuration:
cat > traefik.yml <<'EOF'
api:
insecure: false
dashboard: true
entryPoints:
http:
address: ":80"
https:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
certificateResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /acme.json
httpChallenge:
entryPoint: http
log:
level: INFO
EOF
# Create docker-compose with Let's Encrypt
cat > docker-compose.yml <<'EOF'
version: '3.9'
services:
traefik:
image: traefik:v3.0
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik.yml:/traefik.yml
- ./acme.json:/acme.json
networks:
- traefik
restart: always
web:
image: nginx:alpine
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(\`example.com\`)"
- "traefik.http.routers.web.entrypoints=https"
- "traefik.http.routers.web.tls.certresolver=letsencrypt"
- "traefik.http.services.web.loadbalancer.server.port=80"
networks:
- traefik
networks:
traefik:
EOF
docker-compose up -d
DNS challenge for wildcard certificates:
# DNS challenge configuration (example with Cloudflare)
cat > traefik.yml <<'EOF'
certificateResolvers:
letsencrypt-dns:
acme:
email: [email protected]
storage: /acme.json
dnsChallenge:
provider: cloudflare
delayBeforeCheck: 10
EOF
# Set DNS provider credentials
docker run -d \
--name traefik \
-e [email protected] \
-e CF_API_KEY=your-cloudflare-api-key \
-v ./traefik.yml:/traefik.yml \
traefik:v3.0
# Deploy service with wildcard cert
docker run -d \
--name api \
--label "traefik.enable=true" \
--label "traefik.http.routers.api.rule=Host(\`api.example.com\`) || Host(\`api.*.example.com\`)" \
--label "traefik.http.routers.api.entrypoints=https" \
--label "traefik.http.routers.api.tls.certresolver=letsencrypt-dns" \
--label "traefik.http.services.api.loadbalancer.server.port=8080" \
api:latest
Load Balancing Strategies
Implement different load balancing approaches.
Round-robin load balancing:
# Deploy multiple instances of same service
docker run -d \
--name app1 \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.app.rule=Host(\`app.example.com\`)" \
--label "traefik.http.services.app.loadbalancer.server.port=5000" \
--label "traefik.http.services.app.loadbalancer.passhostheader=true" \
app:latest
docker run -d \
--name app2 \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.app.rule=Host(\`app.example.com\`)" \
--label "traefik.http.services.app.loadbalancer.server.port=5000" \
app:latest
# Traefik automatically round-robins traffic
for i in {1..10}; do curl http://app.example.com; done
Weighted load balancing:
# Via docker-compose labels
version: '3.9'
services:
traefik:
image: traefik:v3.0
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- traefik
app-prod:
image: app:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(\`app.example.com\`)"
- "traefik.http.services.app.loadbalancer.server.port=5000"
- "traefik.http.services.app.loadbalancer.servers.0.url=http://app-prod:5000"
- "traefik.http.services.app.loadbalancer.servers.0.weight=3"
networks:
- traefik
app-canary:
image: app:beta
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(\`app.example.com\`)"
- "traefik.http.services.app.loadbalancer.servers.1.url=http://app-canary:5000"
- "traefik.http.services.app.loadbalancer.servers.1.weight=1"
networks:
- traefik
networks:
traefik:
# 75% traffic to prod, 25% to canary
Middleware Configuration
Apply middleware to modify requests and responses.
Adding request headers:
docker run -d \
--name app \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.app.rule=Host(\`app.example.com\`)" \
--label "traefik.http.routers.app.middlewares=add-headers" \
--label "traefik.http.middlewares.add-headers.headers.customrequestheaders.X-Custom-Header=CustomValue" \
--label "traefik.http.middlewares.add-headers.headers.customresponseheaders.X-Response-Header=ResponseValue" \
--label "traefik.http.services.app.loadbalancer.server.port=5000" \
app:latest
Rate limiting:
docker run -d \
--name api \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.api.rule=Host(\`api.example.com\`)" \
--label "traefik.http.routers.api.middlewares=ratelimit" \
--label "traefik.http.middlewares.ratelimit.ratelimit.average=10" \
--label "traefik.http.middlewares.ratelimit.ratelimit.burst=5" \
--label "traefik.http.services.api.loadbalancer.server.port=8080" \
api:latest
Authentication (Basic Auth):
# Generate htpasswd (username: admin, password: password)
htpasswd -c /tmp/.htpasswd admin
# Get base64 encoded value
cat /tmp/.htpasswd | base64
docker run -d \
--name secure-app \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.http.routers.secure.rule=Host(\`secure.example.com\`)" \
--label "traefik.http.routers.secure.middlewares=basicauth" \
--label "traefik.http.middlewares.basicauth.basicauth.users=admin:passwordhash" \
--label "traefik.http.services.secure.loadbalancer.server.port=5000" \
app:latest
Dashboard and Monitoring
Access Traefik's dashboard and monitoring features.
Enable dashboard:
cat > traefik.yml <<'EOF'
api:
insecure: false
dashboard: true
entryPoints:
traefik:
address: ":8080"
EOF
# Protect dashboard with authentication
docker run -d \
--name traefik \
-p 80:80 \
-p 443:443 \
-p 8080:8080 \
--label "traefik.http.routers.dashboard.rule=Host(\`traefik.example.com\`)" \
--label "traefik.http.routers.dashboard.middlewares=basicauth" \
--label "traefik.http.middlewares.basicauth.basicauth.users=admin:hashedpassword" \
-v /var/run/docker.sock:/var/run/docker.sock \
traefik:v3.0
# Access dashboard
# http://traefik.example.com/dashboard/
Prometheus metrics:
cat > traefik.yml <<'EOF'
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
EOF
# Access metrics
curl http://localhost:8080/metrics
Advanced Features
Implement advanced Traefik functionality.
TCP routing:
# Route TCP traffic (non-HTTP)
docker run -d \
--name tcp-app \
--network traefik \
--label "traefik.enable=true" \
--label "traefik.tcp.routers.mysql.rule=HostSNI(\`*\`)" \
--label "traefik.tcp.routers.mysql.entrypoints=mysql" \
--label "traefik.tcp.services.mysql.loadbalancer.server.port=3306" \
mysql:latest
# Configure TCP entrypoint
cat >> traefik.yml <<'EOF'
entryPoints:
mysql:
address: ":3306"
EOF
Service failover:
# Static file configuration for failover
cat > dynamic.yml <<'EOF'
http:
services:
api-failover:
loadBalancer:
healthChecks:
- uri: /health
interval: 10s
timeout: 5s
servers:
- url: http://api-primary:8080
- url: http://api-secondary:8080
EOF
# Traefik monitors health and fails over
Conclusion
Traefik provides a powerful, modern reverse proxy solution specifically designed for containerized environments. By leveraging Docker service discovery, automatic SSL/TLS management, and flexible middleware configuration, Traefik eliminates manual proxy configuration burden and scales seamlessly with your infrastructure. Whether deploying simple websites or complex microservices architectures, Traefik's label-based configuration and dynamic discovery capabilities provide elegant solutions. Start with basic routing and Let's Encrypt configuration, progress to advanced middleware chains and load balancing strategies, and eventually integrate Traefik's dashboard for operational visibility. As your containerized infrastructure grows, Traefik becomes an indispensable component of your production deployment stack.


