{"slug": "simple-traefik-wildcard-templated-setup", "title": "Simple Traefik Wildcard Templated Setup", "summary": "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.", "body_md": "/root/traefik\n├── .env\n├── compose.yml\n├── services\n│ ├── common\n│ │ ├── headers.yml\n│ │ └── middlewares.yml\n│ ├── photos.yml\n│ ├── server.yml\n│ └── vault.yml\n├── template.yml\n└── traefik.yml\nEnvironment variables\n# Primary domain\nDOMAIN=potat.dev\n# Email for LE expiration notices\nACME_EMAIL=user@example.com\n# Cloudflare API token\nCF_DNS_API_TOKEN=cfut_abc123\n# Timeouts for strict ISP CF_HTTP_TIMEOUT=120 CF_POLLING_INTERVAL=10\n# CF_HTTP_TIMEOUT=120\n# CF_POLLING_INTERVAL=10\n# CF_PROPAGATION_TIMEOUT=300\nToken must have the following permissions:\n- Zone / Zone / Read\n- Zone / DNS / Edit\nIf you are getting failed to create TXT record\nor context deadline exceeded\nerror - uncomment timeouts section\nSource: https://go-acme.github.io/lego/dns/cloudflare\nDocker Compose configuration\nservices:\ntraefik:\nimage: traefik:v3.7\ncontainer_name: traefik\nrestart: unless-stopped\nsecurity_opt:\n- no-new-privileges:true\nenv_file:\n- .env\ncommand:\n- \"--entryPoints.websecure.http.tls.domains[0].main=${DOMAIN}\"\n- \"--entryPoints.websecure.http.tls.domains[0].sans=*.${DOMAIN}\"\nports:\n- \"80:80\"\n- \"443:443\"\nvolumes:\n- ./traefik.yml:/etc/traefik/traefik.yml:ro\n- ./services:/etc/traefik/services:ro\n- certs:/certs\nvolumes:\ncerts:\nname: traefik_certs\nDomain 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\nfile and these parameters\nIf you need an additional domain, add DOMAIN_SECONDARY\nto the .env\nfile and add two more lines using domains[1]\nWarning\nTraefik v2 allowed you to write entryPoints\nas entrypoints\n, but v3 no longer does\nStatic configuration, also known as Install configuration\nglobal:\ncheckNewVersion: true\nsendAnonymousUsage: false\napi:\ndashboard: true\ninsecure: false\nlog:\nlevel: INFO\nentryPoints:\nweb:\naddress: \":80\"\nasDefault: false\nhttp:\nredirections:\nentryPoint:\nto: websecure\nscheme: https\nwebsecure:\naddress: \":443\"\nasDefault: true\nhttp:\ntls:\ncertResolver: cloudflare\nproviders:\nfile:\ndirectory: \"/etc/traefik/services\"\nwatch: true\ncertificatesResolvers:\ncloudflare:\nacme:\nstorage: \"/certs/acme.json\"\ndnsChallenge:\nprovider: cloudflare\npropagation:\n# disablePropagationCheck: true\ndelayBeforeChecks: 10s\nresolvers:\n- \"1.1.1.1:53\"\n- \"8.8.8.8:53\"\n- \"1.0.0.1:53\"\n- \"8.8.4.4:53\"\nserversTransport:\ninsecureSkipVerify: true\ninsecureSkipVerify\nallows using insecure URLs withhttps://\nprotocol and missing SSL certs (e.q. Proxmox)disablePropagationCheck\nskips the DNS propagation checks before notifying ACME that the DNS challenge is readywebsecure.asDefault\nallows you to avoid specifying an entry point for each route\nService configuration template\n{{ $app := \"app\" }}\n{{ $url := \"http://10.0.10.XX:80\" }}\nhttp:\nrouters:\n{{ $app }}-router:\nrule: Host(`{{ $app }}.{{ env \"DOMAIN\" }}`)\nservice: \"{{ $app }}-service\"\nmiddlewares:\n- \"secure-headers\"\n# - \"{{ $app }}-middleware\"\nservices:\n{{ $app }}-service:\nloadBalancer:\nservers:\n- url: \"{{ $url }}\"\npassHostHeader: true\n# middlewares:\n# {{ $app }}-middleware:\n# headers:\n# customRequestHeaders:\n# X-Custom-Header: \"Example\"\nHow to use:\n- Run\ncp template.yml services/service.yml\nto deploy new service - Edit\n$app\nand$url\nvariables -$app\nwill be your subdomain - Custom middleware sections can be removed\nHow this works: Traefik uses Go text/template engine to parse expressions in dynamic configuration\nExample configuration for Proxmox\n{{ $app := \"server\" }}\n{{ $url := \"https://10.0.10.42:8006\" }}\nhttp:\nrouters:\n{{ $app }}-router:\nrule: Host(`{{ $app }}.{{ env \"DOMAIN\" }}`)\nservice: \"{{ $app }}-service\"\nmiddlewares:\n- \"secure-headers\"\nservices:\n{{ $app }}-service:\nloadBalancer:\nservers:\n- url: \"{{ $url }}\"\npassHostHeader: true\nCommon secure headers configuration\nhttp:\nmiddlewares:\nsecure-headers:\nheaders:\nstsSeconds: 31536000\nstsIncludeSubdomains: true\nforceSTSHeader: true\ncontentTypeNosniff: true\nbrowserXssFilter: true\ncustomFrameOptionsValue: SAMEORIGIN", "url": "https://wpnews.pro/news/simple-traefik-wildcard-templated-setup", "canonical_source": "https://gist.github.com/potat-dev/6b3d3db03dc4172ecd03af9e6a3fa4d9", "published_at": "2026-05-22 22:16:14+00:00", "updated_at": "2026-05-22 22:36:42.308535+00:00", "lang": "en", "topics": ["developer-tools", "cloud-computing", "open-source"], "entities": ["Traefik", "Cloudflare", "Docker", "Let's Encrypt"], "alternates": {"html": "https://wpnews.pro/news/simple-traefik-wildcard-templated-setup", "markdown": "https://wpnews.pro/news/simple-traefik-wildcard-templated-setup.md", "text": "https://wpnews.pro/news/simple-traefik-wildcard-templated-setup.txt", "jsonld": "https://wpnews.pro/news/simple-traefik-wildcard-templated-setup.jsonld"}}