Install and configure Traefik reverse proxy with SSL automation

Intermediate 25 min Mar 31, 2026 28 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Set up Traefik as a reverse proxy with Docker Compose for automatic SSL certificate management, service discovery, and load balancing across multiple backend services.

Prerequisites

  • Root or sudo access
  • Docker and Docker Compose installed
  • Domain name pointing to your server
  • Ports 80 and 443 open

What this solves

Traefik is a modern reverse proxy that automatically handles SSL certificates, service discovery, and load balancing. It eliminates manual certificate management by integrating with Let's Encrypt and can route traffic to multiple backend services based on domain names or paths.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions of Docker and dependencies.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install Docker and Docker Compose

Traefik runs as a Docker container, so you need Docker and Docker Compose installed on your system.

sudo apt install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
sudo dnf install -y docker docker-compose
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
Note: Log out and back in for the docker group membership to take effect, or use newgrp docker.

Create Traefik directory structure

Set up the directory structure for Traefik configuration files, SSL certificates, and logs.

mkdir -p ~/traefik/{config,data}
cd ~/traefik
touch data/acme.json
chmod 600 data/acme.json
Never use chmod 777. The ACME file contains private SSL certificates and must only be readable by the owner. Using chmod 600 ensures only you can read/write the file.

Create Docker network

Create a dedicated network for Traefik to communicate with backend services.

docker network create traefik

Create Traefik static configuration

The static configuration defines how Traefik starts up, including API settings, certificate resolvers, and entry points.

global:
  checkNewVersion: false
  sendAnonymousUsage: false

serversTransport:
  insecureSkipVerify: true

api:
  dashboard: true
  debug: true
  insecure: false

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entrypoint:
          to: websecure
          scheme: https
          permanent: true
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: traefik
  file:
    filename: /config/dynamic.yml
    watch: true

certificatesResolvers:
  letsencrypt:
    acme:
      tlsChallenge: {}
      email: admin@example.com
      storage: /data/acme.json
      caServer: https://acme-v02.api.letsencrypt.org/directory
Note: Replace admin@example.com with your actual email address for Let's Encrypt notifications.

Create dynamic configuration

Dynamic configuration handles middleware, routers, and services that can change without restarting Traefik.

http:
  middlewares:
    default-headers:
      headers:
        frameDeny: true
        sslRedirect: true
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: SAMEORIGIN
        customRequestHeaders:
          X-Forwarded-Proto: https
    
    secure-headers:
      headers:
        accessControlAllowMethods:
          - GET
          - OPTIONS
          - PUT
        accessControlMaxAge: 100
        hostsProxyHeaders:
          - "X-Forwarded-Host"
        referrerPolicy: "same-origin"
    
    auth:
      basicAuth:
        users:
          - "admin:$2y$10$2b2cu2Fw1ZfqmVnBKgxNj.e7wjHyBhgDw4Y2H9GKkJvUn2k4y7BL2"

tls:
  options:
    default:
      sslVersions:
        - "TLSv1.2"
        - "TLSv1.3"
      cipherSuites:
        - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
        - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
        - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
Warning: The password hash shown is for "admin123". Generate your own with htpasswd -nb admin yourpassword.

Create Docker Compose file

This compose file defines the Traefik service with proper volume mounts and network configuration.

version: '3.7'

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - traefik
    ports:
      - "80:80"
      - "443:443"
    environment:
      - CF_API_EMAIL=admin@example.com
      - CF_DNS_API_TOKEN=your_cloudflare_token_here
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/data/acme.json
      - ./config:/config:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.rule=Host(traefik.example.com)"
      - "traefik.http.routers.traefik.tls=true"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$2y$$10$$2b2cu2Fw1ZfqmVnBKgxNj.e7wjHyBhgDw4Y2H9GKkJvUn2k4y7BL2"
      - "traefik.http.routers.traefik.middlewares=traefik-auth"
      - "traefik.http.routers.traefik.service=api@internal"

networks:
  traefik:
    external: true
Note: Replace traefik.example.com with your actual domain name where you want to access the Traefik dashboard.

Configure firewall rules

Open the necessary ports for HTTP and HTTPS traffic in your system firewall.

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload

Start Traefik

Launch Traefik using Docker Compose and verify it starts successfully.

cd ~/traefik
docker compose up -d
docker compose logs -f traefik

Deploy a test service

Create a simple test application to verify Traefik routing and SSL certificate generation work correctly.

version: '3.7'

services:
  whoami:
    image: traefik/whoami
    container_name: whoami
    restart: unless-stopped
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.rule=Host(test.example.com)"
      - "traefik.http.routers.whoami.tls=true"
      - "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
      - "traefik.http.middlewares.whoami.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.routers.whoami.middlewares=whoami"

networks:
  traefik:
    external: true
docker compose -f test-app.yml up -d

Configure advanced routing and load balancing

Set up path-based routing

Configure Traefik to route different paths to different services, useful for microservices architectures.

http:
  routers:
    api-router:
      rule: "Host(app.example.com) && PathPrefix(/api)"
      service: api-service
      middlewares:
        - strip-api-prefix
        - secure-headers
      tls:
        certResolver: letsencrypt
    
    web-router:
      rule: "Host(app.example.com) && PathPrefix(/)"
      service: web-service
      priority: 1
      middlewares:
        - secure-headers
      tls:
        certResolver: letsencrypt

  middlewares:
    strip-api-prefix:
      stripPrefix:
        prefixes:
          - "/api"

  services:
    api-service:
      loadBalancer:
        servers:
          - url: "http://api-container:3000"
        healthCheck:
          path: "/health"
          interval: "30s"
          timeout: "5s"
    
    web-service:
      loadBalancer:
        servers:
          - url: "http://web-container:8080"
          - url: "http://web-container-2:8080"
        sticky:
          cookie:
            name: "traefik-session"
            secure: true
            httpOnly: true

Configure rate limiting middleware

Add rate limiting to protect your services from abuse and ensure fair resource usage.

http:
  middlewares:
    rate-limit:
      rateLimit:
        burst: 100
        average: 50
        period: "1m"
        sourceCriterion:
          ipStrategy:
            depth: 1
    
    api-rate-limit:
      rateLimit:
        burst: 20
        average: 10
        period: "1m"
    
    compress:
      compress:
        excludedContentTypes:
          - "text/event-stream"
    
    cors:
      headers:
        accessControlAllowCredentials: true
        accessControlAllowHeaders:
          - "*"
        accessControlAllowMethods:
          - "GET"
          - "POST"
          - "PUT"
          - "DELETE"
          - "OPTIONS"
        accessControlAllowOriginList:
          - "https://example.com"
          - "https://app.example.com"
        accessControlMaxAge: 86400

Set up monitoring and observability

Enable metrics collection

Configure Traefik to expose Prometheus metrics for monitoring and alerting.

metrics:
  prometheus:
    addEntryPointsLabels: true
    addServicesLabels: true
    addRoutersLabels: true
    buckets:
      - 0.1
      - 0.3
      - 1.2
      - 5.0

tracing:
  jaeger:
    samplingParam: 1.0
    samplingType: const
    localAgentHostPort: "jaeger:6831"

log:
  level: INFO
  filePath: "/data/traefik.log"
  format: json

accessLog:
  filePath: "/data/access.log"
  format: json
  bufferingSize: 100
  filters:
    statusCodes:
      - "400-499"
      - "500-599"
    retryAttempts: true
    minDuration: "10ms"

Add log rotation

Set up logrotate to manage Traefik log files and prevent disk space issues.

sudo tee /etc/logrotate.d/traefik > /dev/null << 'EOF'
/home/$USER/traefik/data/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
    su $USER $USER
}
EOF

Verify your setup

Check that Traefik is running correctly and generating SSL certificates automatically.

docker compose ps
docker compose logs traefik | grep -i certificate
curl -I https://test.example.com
openssl s_client -connect test.example.com:443 -servername test.example.com < /dev/null 2>/dev/null | openssl x509 -noout -issuer -dates

Visit your Traefik dashboard at https://traefik.example.com and verify you can see your services listed. Check that SSL certificates are valid and automatically renewed.

Common issues

SymptomCauseFix
Certificate generation failsDomain not pointing to serverVerify DNS records point to your server's public IP
Dashboard returns 404Wrong host rule or missing labelsCheck docker-compose.yml labels match your domain
Backend service unreachableNot on traefik networkAdd service to traefik network: docker network connect traefik container-name
Rate limit not workingClient behind proxyConfigure ipStrategy depth to match proxy layers
Logs show permission deniedWrong file ownershipchown -R $USER:$USER ~/traefik/data
SSL redirect loopConflicting redirect rulesRemove duplicate redirections from dynamic config

Next steps

Automated install script

Run this to automate the entire setup

#traefik #reverse-proxy #docker-compose #ssl-automation #load-balancer

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer