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

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.