Traefik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy. It integrates with your existing infrastructure (Docker, Swarm, Kubernetes, Consul, …) and configures itself automatically and dynamically - routers, services, and TLS certs appear from container labels or watched config files, no restarts.

This is my working v3 setup, plus recipes for the things you always end up needing: wildcard certs, proxying non-Docker services, Basic Auth, and Google SSO.

Base setup

docker-compose.yaml

name: traefik-localhost
 
services:
  traefik:
    image: traefik:3
    container_name: traefik.localhost
    restart: always
    mem_limit: 256M
    volumes:
      - ./:/opt/traefik
      - ./:/etc/traefik
    network_mode: host
    labels:
      - com.centurylinklabs.watchtower.enable=true

traefik.yaml (static config)

# Traefik v3.x main configuration - /opt/traefik/traefik.yaml
 
# Listen on http/https; force http → https.
entryPoints:
  https:
    address: ':443'
  http:
    address: ':80'
    http:
      redirections:
        entryPoint:
          to: https
          scheme: https
  ssh:
    address: ':2222'
  monitoring:
    address: ':9100'
 
# Let's Encrypt - HTTP challenge (see recipe 1 for the DNS/wildcard variant)
certificatesResolvers:
  lets-encrypt:
    acme:
      email: noreply@example.com
      storage: /opt/traefik/acme/acme.json
      httpChallenge:
        entryPoint: http
 
metrics:
  prometheus:
    addServicesLabels: true
    addEntryPointsLabels: true
    entryPoint: monitoring
 
accessLog:
  filePath: /dev/stdout
 
providers:
  file:
    directory: /opt/traefik/conf.d
    watch: true
  docker:
    exposedByDefault: false
    endpoint: "tcp://localhost:2375"
    watch: true

conf.d/00-common.yaml (secure TLS defaults)

tls:
  options:
    default:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
        - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256

The file provider watches conf.d/ - drop a .yaml in and Traefik hot-reloads it. Everything below is a conf.d/*.yaml fragment (or a static-config / compose edit where noted).


Recipe 1 - Cloudflare DNS challenge (wildcard certs)

The HTTP challenge can’t issue wildcard certificates and needs port 80 reachable from the internet. The DNS challenge solves both - it proves domain ownership via a Cloudflare DNS record.

In traefik.yaml, replace the resolver’s httpChallenge with:

certificatesResolvers:
  lets-encrypt:
    acme:
      email: noreply@example.com
      storage: /opt/traefik/acme/acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"

In docker-compose.yaml, give Traefik a scoped Cloudflare API token (create it in Cloudflare → My Profile → API Tokens with Zone → DNS → Edit):

    environment:
      - CF_DNS_API_TOKEN=your-cloudflare-api-token

Then request a wildcard on any router’s TLS block:

tls:
  certResolver: lets-encrypt
  domains:
    - main: "example.com"
      sans:
        - "*.example.com"

Recipe 2 - Reverse proxy to an external (non-Docker) service

For a box Traefik can’t auto-discover (a NAS, a VM, a device on the LAN), declare it with the file provider. conf.d/10-external.yaml:

http:
  routers:
    nas:
      rule: "Host(`nas.example.com`)"
      entryPoints:
        - https
      service: nas
      tls:
        certResolver: lets-encrypt
  services:
    nas:
      loadBalancer:
        servers:
          - url: "http://192.168.1.50:8080"   # the real backend

Add more url: lines under servers: and Traefik load-balances across them.


Recipe 3 - Basic Auth

A quick password gate (e.g. for a dashboard). Generate a hash with htpasswd -nB admin (from apache2-utils). conf.d/20-auth.yaml:

http:
  middlewares:
    basic-auth:
      basicAuth:
        users:
          - "admin:$apr1$xxxxxxxx$yyyyyyyyyyyyyyyyyyyyy."

Gotcha: in a file (like this), paste the hash as-is. In a docker label, every $ must be doubled to $$ or compose will try to expand it.

Attach it to any router:

http:
  routers:
    dashboard:
      rule: "Host(`traefik.example.com`)"
      entryPoints: [https]
      service: api@internal
      middlewares:
        - basic-auth
      tls:
        certResolver: lets-encrypt

Recipe 4 - Google SSO via traefik-forward-auth

For real SSO (login with Google, allowlist emails), front your services with traefik-forward-auth. It’s a tiny auth service Traefik consults on every request via a forwardAuth middleware.

1. Google OAuth client - in Google Cloud Console → APIs & Services → Credentials, create an OAuth 2.0 Client ID (Web application) with the authorized redirect URI: https://auth.example.com/_oauth.

2. Add the service to docker-compose.yaml:

  traefik-forward-auth:
    image: thomseddon/traefik-forward-auth:2
    container_name: traefik-forward-auth.localhost
    restart: always
    ports:
      - "4181:4181"           # host-networked Traefik reaches it on localhost
    environment:
      - DEFAULT_PROVIDER=google
      - PROVIDERS_GOOGLE_CLIENT_ID=your-id.apps.googleusercontent.com
      - PROVIDERS_GOOGLE_CLIENT_SECRET=your-secret
      - SECRET=change-me-to-a-random-string     # signs the auth cookie
      - AUTH_HOST=auth.example.com
      - COOKIE_DOMAIN=example.com
      - WHITELIST=you@gmail.com                  # allowed emails (comma-separated)

3. Define the middleware in conf.d/30-forward-auth.yaml:

http:
  middlewares:
    google-auth:
      forwardAuth:
        address: "http://localhost:4181"   # Traefik uses host networking here
        authResponseHeaders:
          - X-Forwarded-User
        trustForwardHeader: true

4. Protect any router by adding the middleware:

      middlewares:
        - google-auth

Now hitting that host bounces you through Google login; only allowlisted emails get in, and the authenticated user arrives in the X-Forwarded-User header.