Simple Traefik Wildcard Templated Setup This article describes a Traefik reverse proxy setup that uses environment variables and Go template files to dynamically configure wildcard SSL certificates and multiple services. The configuration includes a static `traefik.yml` file, a `template.yml` for easily deploying new services by copying and editing variables, and a Cloudflare DNS challenge for automatic Let's Encrypt certificate generation. The setup also provides example configurations for common secure headers and services like Proxmox. /root/traefik ├── .env ├── compose.yml ├── services │ ├── common │ │ ├── headers.yml │ │ └── middlewares.yml │ ├── photos.yml │ ├── server.yml │ └── vault.yml ├── template.yml └── traefik.yml Environment variables Primary domain DOMAIN=potat.dev Email for LE expiration notices ACME EMAIL=user@example.com Cloudflare API token CF DNS API TOKEN=cfut abc123 Timeouts for strict ISP CF HTTP TIMEOUT=120 CF POLLING INTERVAL=10 CF HTTP TIMEOUT=120 CF POLLING INTERVAL=10 CF PROPAGATION TIMEOUT=300 Token must have the following permissions: - Zone / Zone / Read - Zone / DNS / Edit If you are getting failed to create TXT record or context deadline exceeded error - uncomment timeouts section Source: https://go-acme.github.io/lego/dns/cloudflare Docker Compose configuration services: traefik: image: traefik:v3.7 container name: traefik restart: unless-stopped security opt: - no-new-privileges:true env file: - .env command: - "--entryPoints.websecure.http.tls.domains 0 .main=${DOMAIN}" - "--entryPoints.websecure.http.tls.domains 0 .sans= .${DOMAIN}" ports: - "80:80" - "443:443" volumes: - ./traefik.yml:/etc/traefik/traefik.yml:ro - ./services:/etc/traefik/services:ro - certs:/certs volumes: certs: name: traefik certs Domain settings are configured here because static configuration does not support environment variable substitution, whereas this method does. Static configuration is being merged together from the traefik.yml file and these parameters If you need an additional domain, add DOMAIN SECONDARY to the .env file and add two more lines using domains 1 Warning Traefik v2 allowed you to write entryPoints as entrypoints , but v3 no longer does Static configuration, also known as Install configuration global: checkNewVersion: true sendAnonymousUsage: false api: dashboard: true insecure: false log: level: INFO entryPoints: web: address: ":80" asDefault: false http: redirections: entryPoint: to: websecure scheme: https websecure: address: ":443" asDefault: true http: tls: certResolver: cloudflare providers: file: directory: "/etc/traefik/services" watch: true certificatesResolvers: cloudflare: acme: storage: "/certs/acme.json" dnsChallenge: provider: cloudflare propagation: disablePropagationCheck: true delayBeforeChecks: 10s resolvers: - "1.1.1.1:53" - "8.8.8.8:53" - "1.0.0.1:53" - "8.8.4.4:53" serversTransport: insecureSkipVerify: true insecureSkipVerify allows using insecure URLs withhttps:// protocol and missing SSL certs e.q. Proxmox disablePropagationCheck skips the DNS propagation checks before notifying ACME that the DNS challenge is readywebsecure.asDefault allows you to avoid specifying an entry point for each route Service configuration template {{ $app := "app" }} {{ $url := "http://10.0.10.XX:80" }} http: routers: {{ $app }}-router: rule: Host {{ $app }}.{{ env "DOMAIN" }} service: "{{ $app }}-service" middlewares: - "secure-headers" - "{{ $app }}-middleware" services: {{ $app }}-service: loadBalancer: servers: - url: "{{ $url }}" passHostHeader: true middlewares: {{ $app }}-middleware: headers: customRequestHeaders: X-Custom-Header: "Example" How to use: - Run cp template.yml services/service.yml to deploy new service - Edit $app and$url variables -$app will be your subdomain - Custom middleware sections can be removed How this works: Traefik uses Go text/template engine to parse expressions in dynamic configuration Example configuration for Proxmox {{ $app := "server" }} {{ $url := "https://10.0.10.42:8006" }} http: routers: {{ $app }}-router: rule: Host {{ $app }}.{{ env "DOMAIN" }} service: "{{ $app }}-service" middlewares: - "secure-headers" services: {{ $app }}-service: loadBalancer: servers: - url: "{{ $url }}" passHostHeader: true Common secure headers configuration http: middlewares: secure-headers: headers: stsSeconds: 31536000 stsIncludeSubdomains: true forceSTSHeader: true contentTypeNosniff: true browserXssFilter: true customFrameOptionsValue: SAMEORIGIN