Traefik

Traefik is a reverse proxy and load balancer that automatically discovers Docker services and configures routing, TLS termination, and middleware dynamically.

This compose file deploys Traefik with HTTP-to-HTTPS redirection, automatic TLS certificate provisioning via a Cloudflare DNS challenge, and Docker provider auto-discovery. The dashboard is protected with HTTP basic authentication. Prometheus metrics, access logging, and health checks are enabled.

For information on setting up basic authentication and IP whitelisting for the Traefik dashboard, check out this blog post.

Docker Compose

# compose.yaml

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      #  - 8080:8080
    networks:
      - traefik
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - type: volume
        source: docker-nfs
        target: /certs
        volume:
          subpath: traefik/certs
    security_opt:
      - no-new-privileges:true
    command:
      # EntryPoints
      # web - HTTP
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
      - "--entrypoints.web.forwardedheaders.trustedips=${TRAEFIK_TRUSTED_IPS}"
      # websecure - HTTPS
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.websecure.http.tls=true"
      - "--entrypoints.websecure.forwardedheaders.trustedips=${TRAEFIK_TRUSTED_IPS}"

      # Disable SSL Verification for self-signed certs
      - "--serversTransport.insecureSkipVerify=true"

      # DNS Challenge
      - "--certificatesresolvers.cloudflare.acme.dnschallenge=true"
      - "--certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare"
      - "--certificatesresolvers.cloudflare.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53"
      - "--certificatesresolvers.cloudflare.acme.email=${CF_API_EMAIL}"
      - "--certificatesresolvers.cloudflare.acme.storage=/certs/acme.json"

      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik"
      # Providers file
      - "--providers.file.filename=/dynamic/providers.yaml"

      # API & Dashboard
      - "--api.dashboard=true"
      - "--api.insecure=false"

      # Observability
      - "--log.level=DEBUG"
      - "--accesslog=true"
      - "--metrics.prometheus=true"
      - "--ping=true"
    environment:
      CF_DNS_API_TOKEN: ${CF_DNS_API_TOKEN}
      TZ: Europe/London
    labels:
      # Enable self‑routing
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"

      # Dashboard router
      - "traefik.http.routers.dashboard.rule=Host(`traefik.${TRAEFIK_BASE_URL}`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.tls=true"
      - "traefik.http.routers.dashboard.tls.certresolver=cloudflare"

      # Basic‑auth middleware
      - "traefik.http.middlewares.auth.basicauth.users=${BASIC_AUTH_USER_PASS}"
      - "traefik.http.routers.dashboard.middlewares=auth@docker"
      - "traefik.http.middlewares.auth.basicauth.headerField=X-Remote-User"

      # HTTPS headers middleware
      - "traefik.http.middlewares.https-header.headers.customrequestheaders.X-Forwarded-Proto=https"

networks:
  traefik:
    name: traefik

volumes:
  docker-nfs:
    driver: local
    driver_opts:
      type: nfs
      o: addr=xxx.xxx.xxx.xxx,nolock,soft,rw,nfsvers=4.2
      device: :/mnt/nfs-volume

Environment Variables

# .env

CF_API_EMAIL=
CF_DNS_API_TOKEN=
BASIC_AUTH_USER_PASS= # generate with `htpasswd -nb user password`
TRAEFIK_TRUSTED_IPS=
TRAEFIK_BASE_URL=example.com