Files
traefik/README.md
T

8.8 KiB
Raw Blame History

Traefik reverse proxy stack

Docker Compose stack for Traefik with Lets Encrypt (Cloudflare DNS challenge), a secured dashboard, and a reusable middleware library for security headers, CSP tiers, Authentik SSO, redirects, and IP allowlists.

Prerequisites

  • Docker Engine with Compose v2
  • A Cloudflare account with API access for the domains you terminate here
  • DNS for those domains pointed at the host that runs this stack
  • A shared Docker network that your backend services also join (default: docknet)

Quick start

git clone <this-repo> traefik && cd traefik
cp example.env .env

Edit .env (see Environment variables), then:

docker compose --env-file .env up -d

Traefik will join (or create) the Docker network named by NETWORK_NAME, watch containers on that host for Traefik labels, and issue certificates via Cloudflare DNS. ACME data is stored in the Docker named volume traefik-acme (created automatically; no manual file setup).

Layout

.
├── compose.yml              # Traefik service, ports, dashboard labels, ACME volume
├── example.env              # Env template (copy to .env)
└── traefik/
    ├── traefik.yaml         # Static config: entrypoints, providers, TLS SANs
    └── conf.d/
        ├── middlewares.yaml # Shared middleware library
        ├── routers.yaml     # Optional file-based routers
        └── services.yaml    # Optional file-based services

Static config lives in traefik/traefik.yaml. Dynamic config (middlewares, optional routers/services) is loaded from traefik/conf.d/ and hot-reloads when files change. Certificate state lives in the traefik-acme volume, not in the repo.

Environment variables

Copy example.env to .env and fill in real values:

Variable Purpose
CLOUDFLARE_EMAIL Cloudflare account email (ACME + API)
CLOUDFLARE_API_KEY Cloudflare Global API Key (or scoped token with DNS edit)
CONSOLE_HOST Hostname for the Traefik dashboard (e.g. traefik.example.com)
NETWORK_NAME Docker network name shared with backends (default docknet)
TRAEFIK_CONSOLE_AUTH Basic-auth users for the dashboard (htpasswd format)
TRAEFIK_YAML Host path to static config (default ./traefik/traefik.yaml)
TRAEFIK_CONFD Host path to dynamic config dir (default ./traefik/conf.d)

Generate dashboard credentials:

# Install apache2-utils / httpd-tools if needed, then:
htpasswd -nbB admin 'your-strong-password'

Paste the output into TRAEFIK_CONSOLE_AUTH. If the value contains $, escape them for Compose (use $$) or quote carefully in .env.

Ports

Host Container Role
8342 80 HTTP → redirects to HTTPS
8767 443 HTTPS
8080 8080 Traefik API / dashboard (also exposed via CONSOLE_HOST with TLS + basic auth)

Point your public reverse path or firewall at 8767 (and 8342 if you want HTTP→HTTPS at the edge). Adjust the left-hand ports in compose.yml if you prefer 80/443 on the host.

TLS and certificates

  • Challenge: DNS-01 via Cloudflare (certificatesResolvers.letsencrypt)
  • Storage: Docker named volume traefik-acme mounted at /letsencrypt (Traefik creates acme.json on first run)
  • Default certificate SANs for the TLS store are defined under tls.stores.default in traefik/traefik.yaml — edit that list for your domains

The volume survives docker compose down / up. Prefer docker compose down over docker compose down -v unless you intentionally want to wipe certs (which will re-hit Lets Encrypt rate limits).

Ensure Cloudflare credentials can create/delete _acme-challenge TXT records for every zone you serve.

Dashboard

The dashboard is enabled and routed with Docker labels on the Traefik container:

  • URL: https://<CONSOLE_HOST>/
  • Entrypoint: websecure
  • Auth: middleware traefik-auth (basic auth from TRAEFIK_CONSOLE_AUTH)
  • Certificate: Lets Encrypt via letsencrypt resolver

Using middlewares on your services

Connect apps to the same Docker network (NETWORK_NAME), enable Traefik, and attach middlewares by name with the @file provider:

services:
  myapp:
    image: myapp:latest
    networks:
      - docknet
    labels:
      - traefik.enable=true
      - traefik.http.routers.myapp.rule=Host(`app.example.com`)
      - traefik.http.routers.myapp.entrypoints=websecure
      - traefik.http.routers.myapp.tls.certresolver=letsencrypt
      - traefik.http.routers.myapp.middlewares=security-supermax@file,gzip@file
      - traefik.http.services.myapp.loadbalancer.server.port=8080

networks:
  docknet:
    external: true
    name: docknet

Chain multiple middlewares with commas. File-provider names resolve as <name>@file.

Middleware catalog

Defined in traefik/conf.d/middlewares.yaml.

Security chains (pick one)

Middleware Intent
default CSP strict + frame deny + gzip + HSTS + baseline security headers
secure-all Permissive CSP + same-origin frames + HSTS + security headers
security-mallcop Open referrer + open CSP + HSTS + security headers
security-lax Lax referrer + lax CSP + HSTS + security headers
security-medium Permissive referrer/CSP + same-origin frames + HSTS + security headers
security-prison Medium referrer + balanced CSP + same-origin frames + HSTS + security headers
security-supermax No-referrer + strict CSP + frame deny + HSTS + security headers
security-supermax-with-analytics Supermax plus GA/Clarity/Umami-friendly CSP and COOP

CSP building blocks

Middleware Notes
csp-open-header Very permissive (legacy / hard-to-lock apps)
csp-lax-header Self + common inline/eval allowances
csp-permissive-header Self-focused with inline/eval for scripts/styles
csp-balanced-header Self + common CDNs (jsDelivr, cdnjs, unpkg, Google Fonts)
csp-strict-header Self-only
csp-strict-header-with-ga Strict plus analytics/Clarity/GTM endpoints

Other headers & utilities

Middleware Role
security-headers X-Content-Type-Options: nosniff, disables legacy X-XSS-Protection
hsts-header HSTS 2y, includeSubDomains, preload
frame-deny-header X-Frame-Options: DENY
frame-sameorigin-header X-Frame-Options: SAMEORIGIN
cross-origin-opener-strict-policy Cross-Origin-Opener-Policy: same-origin
gzip Response compression
referrer-*-header Referrer-Policy tiers (opensupermax)
strip-server-path Strips /db, /server, /traefik prefixes

Auth & access

Middleware Role
authentik-outpost ForwardAuth to Authentik outpost (outpost.mifi.holdings)
authentik-no-cache Disable caching on Authentik responses
authentik-static-immutable Long-cache immutable static assets
internal-ips IP allowlist (edit sourceRange for your networks)

Redirects

Middleware Role
redirect-www-to-non-www www. → apex
redirect-non-www-to-www apex → www.
redirect-postfixadmin Mail settings path redirect (site-specific)

Update Authentik outpost URL, IP allowlists, and domain-specific redirects in middlewares.yaml before deploying outside this environment.

File-based routers and services

traefik/conf.d/routers.yaml and services.yaml are stubs for optional non-Docker backends. Uncomment or add entries when you need host/path rules that are not expressed as container labels.

Operations

# Start / recreate after config or .env changes
docker compose --env-file .env up -d

# Follow logs
docker compose logs -f traefik

# Stop (keeps the ACME volume)
docker compose down

# Inspect ACME volume location (optional)
docker volume inspect traefik_traefik-acme

Dynamic files under conf.d/ reload without a full restart. Changes to traefik.yaml, Compose labels, ports, or env vars generally need docker compose up -d (or a container recreate).

Customization checklist

  1. Set .env from example.env and generate TRAEFIK_CONSOLE_AUTH
  2. Edit TLS SANs in traefik/traefik.yaml for your domains
  3. Adjust internal-ips and Authentik URLs in middlewares.yaml
  4. Ensure backends use traefik.enable=true and join NETWORK_NAME
  5. Publish host ports (8342/8767) as needed for your network edge

Security notes

  • Do not commit .env or real API keys
  • Prefer a scoped Cloudflare API token limited to DNS edit on required zones when possible
  • Keep the dashboard behind strong basic auth (and consider further restricting with internal-ips or Authentik)
  • Review CSP chains per app; start stricter and loosen only where breakage requires it
  • Do not use docker compose down -v in production unless you mean to discard certificates