traefik
The traefik role runs Traefik and a read-only Docker socket proxy as one compose project. Containers join the shared network and opt in with traefik.enable=true. Traefik never sees the Docker socket itself; it talks to the proxy, which allows only reads of containers and networks.
What it does
Section titled “What it does”- Creates the project directory (
traefik_dir,/opt/traefik) with a0700acmedirectory for certificates. - Writes the DNS provider environment to
dns.envwith mode0600, and never logs its contents. - Writes the static configuration, the compose file, and the files you give in
traefik_dynamic_filesfor the file provider. - Starts the project with Docker Compose. A change of the static configuration or the DNS environment restarts Traefik.
- Creates a shared network (
proxy,172.31.255.0/24) and gives Traefik a fixed address on it (172.31.255.254), outside the range Docker assigns from, so a backend can trust exactly that address.
Certificates come from ACME. By default the role uses the DNS-01 challenge, which allows wildcard certificates and needs no port open to the internet. With traefik_acme_challenge: http it uses HTTP-01 instead; see HTTP-01 certificates.
Entrypoints
Section titled “Entrypoints”traefik_entrypoints is a list of dicts with these keys:
| Key | Meaning |
|---|---|
name | Entrypoint name. |
port | Container port, and host port unless host_port is set. |
host_port | Port published on the host (default: port). Lets two entrypoints share a host port on different bind addresses. A redirect to such an entrypoint goes to its host_port. |
bind | Host addresses to publish on. Empty publishes on all. |
protocol | tcp (default) or udp. |
tls | Terminate TLS with the certificate resolver. |
as_default | Routers without an explicit entrypoint use this one. |
redirect_to | Redirect HTTP on this entrypoint to another entrypoint. |
middlewares | Middlewares every router on this entrypoint gets, for example [name@file]. Not allowed together with redirect_to. |
The default is web on port 80, redirecting to websecure on port 443, which terminates TLS and is the default.
Key variables
Section titled “Key variables”| Variable | Default | Meaning |
|---|---|---|
traefik_image | pinned traefik v3 tag | Traefik image. |
traefik_acme_email | empty | ACME account e-mail; required when an entrypoint has tls on. |
traefik_acme_ca_server | Let’s Encrypt production | ACME directory. Use the staging directory for tests. |
traefik_acme_challenge | dns | dns (DNS-01) or http (HTTP-01). Also the name of the cert resolver. |
traefik_acme_http_entrypoint | web | HTTP-01 only. Entrypoint that answers the challenge; it must listen on host port 80. |
traefik_dns_provider | hetzner | lego DNS provider of the DNS-01 challenge. |
traefik_dns_env | {} | Environment of the DNS provider, usually an API token. |
traefik_cert_domains | [] | Certificates to request: main and sans. |
traefik_network | proxy | Shared network name that other roles join. |
traefik_access_log | false | Write an access log to stdout. |
traefik_docker_allow_empty_services | false | Keep the routers of containers without a healthy server instead of letting them disappear. See Error pages. |
traefik_error_pages_enabled | false | Route errors to an error pages service. See Error pages. |
traefik_dynamic_files | {} | File-provider files, name to YAML content. |
All variables: traefik in the reference.
Example
Section titled “Example”- hosts: edge become: true roles: - noixdev.infra.docker - role: noixdev.infra.traefik vars: traefik_acme_email: ops@example.com traefik_dns_provider: hetzner traefik_dns_env: HETZNER_API_TOKEN: "{{ vault_dns_token }}" traefik_cert_domains: - main: example.com sans: ["*.example.com"] traefik_dynamic_files: headers.yml: http: middlewares: secure-headers: headers: stsSeconds: 31536000Another container uses Traefik by joining the proxy network and setting labels such as traefik.enable=true and a router rule. The portainer and pulsix_manager roles do this for you when you set their *_host variable.
HTTP-01 certificates
Section titled “HTTP-01 certificates”With traefik_acme_challenge: http Traefik proves control of a name by answering a request on port 80, so no DNS API token is needed. Traefik requests one certificate per router host, when a router first uses the name. The resolver is named after the challenge: http here, dns by default.
- hosts: edge become: true roles: - noixdev.infra.docker - role: noixdev.infra.traefik vars: traefik_acme_email: ops@example.com traefik_acme_challenge: http traefik_acme_http_entrypoint: webThe default entrypoints already fit: web listens on host port 80 and redirects to websecure.
Requirements, which the role checks before it changes anything:
traefik_acme_http_entrypointnames an existing entrypoint whose host port is 80 (host_port, orportwhenhost_portis not set).traefik_cert_domainscontains no wildcard. HTTP-01 cannot issue them; use the DNS-01 challenge for wildcards.
Each host must also resolve publicly to this server, and port 80 must be reachable from the internet. An entrypoint on a private address cannot answer the challenge.
Error pages
Section titled “Error pages”With traefik_error_pages_enabled: true the role routes errors to an error pages service that serves /<code>.html. By default it also runs that service: the error-pages container (traefik_error_pages_image, default noixdev/error-pages:1) as the compose service error-pages in the Traefik project, with the alias error-pages on the shared network. It runs read-only as user 65532, without capabilities, with the image’s healthcheck, CPU and memory limits (traefik_error_pages_cpus, traefik_error_pages_memory) and a volume on /data for maintenance mode.
The container’s environment is traefik_error_pages_env. It replaces the defaults as a whole (theme glass, languages en,de), so list every variable you want. See the error-pages configuration for the options.
To run the service somewhere else, set traefik_error_pages_container: false and point traefik_error_pages_url at it. The default URL is the bundled container, http://error-pages:8080.
What gets routed
Section titled “What gets routed”The role writes dynamic/error-pages.yml for the file provider with:
- the middleware
error-pages-errors, anerrorsmiddleware that asks the service for/{status}.htmlfor every status intraefik_error_pages_status(default["502-504"]); - the middleware
error-pages-compress, limited totraefik_error_pages_compress_types, unlesstraefik_error_pages_compressisfalse. Each page is 100-200 KB, so keep it on; - a router
error-pages-<name>for each entry oftraefik_error_pages_routes, answering with one fixed page through areplacePathmiddleware, so a visitor cannot pick another page by path; - the service
error-pages.
Every router on the entrypoints listed in traefik_error_pages_entrypoints gets error-pages-compress and error-pages-errors, before the entrypoint’s own middlewares. Those entrypoints must exist and must not redirect.
Each route has these keys:
| Key | Meaning |
|---|---|
name | Required. [a-z0-9-]+, unique. |
entrypoints | Required. Entrypoints the router listens on. |
rule | Router rule. Default HostRegexp(.+). |
priority | Higher wins. Default 1. |
page | A status code or internal. Default 404. The page is requested as /<page>.html. |
A catch-all route with the lowest priority answers unknown hosts with the 404 page. A route with page: internal and a higher priority is a guard: it answers a name that must not be served on an entrypoint with the “internal only” page.
Example
Section titled “Example”- hosts: edge become: true roles: - noixdev.infra.docker - role: noixdev.infra.traefik vars: traefik_acme_email: ops@example.com traefik_dns_env: HETZNER_API_TOKEN: "{{ vault_dns_token }}" traefik_cert_domains: - main: example.com sans: ["*.example.com"] traefik_entrypoints: - name: web port: 80 redirect_to: websecure - name: websecure port: 443 bind: [192.0.2.10] tls: true as_default: true - name: internal port: 8443 host_port: 443 bind: [198.51.100.10] tls: true traefik_docker_allow_empty_services: true traefik_error_pages_enabled: true traefik_error_pages_entrypoints: [websecure, internal] traefik_error_pages_routes: - name: catchall entrypoints: [websecure, internal] - name: admin-guard entrypoints: [websecure] rule: Host(`admin.example.com`) priority: 100 page: internalHere websecure is public and internal shares host port 443 on a private address. The guard answers admin.example.com on the public entrypoint with the internal-only page, and the catch-all answers every other unknown host with 404.
Unhealthy backends
Section titled “Unhealthy backends”With traefik_docker_allow_empty_services: true a container without a healthy server keeps its router, so a request is not handled by the catch-all and answered with a misleading 404. The role’s variable description says the router answers 503. With Traefik 3.7 it answers 500 instead, and the default 502-504 status list does not catch that. You can add 500 to traefik_error_pages_status, but then every 500 from every application is replaced by the error page and the application’s own error body is lost. Add it only if you accept that. See also the error-pages Traefik guide.
Maintenance mode
Section titled “Maintenance mode”The container’s volume holds the maintenance flag. Switch it with the container’s own command. The container name is <compose project>-error-pages-1, so traefik-error-pages-1 with the default traefik_project:
docker exec traefik-error-pages-1 /error-pages maintenance ondocker exec traefik-error-pages-1 /error-pages maintenance offdocker exec traefik-error-pages-1 /error-pages maintenance statusDisabling
Section titled “Disabling”Set traefik_error_pages_enabled: false (the default). The role removes dynamic/error-pages.yml after Traefik has restarted without the middlewares, and stops writing the container. Remove the entrypoint middlewares and any router labels of your own that refer to error-pages-*@file.
Gotchas
Section titled “Gotchas”- Traefik needs Docker on the host. Apply
dockerfirst. traefik_acme_emailis required with TLS. The role stops with a message when an entrypoint hastls: trueand the e-mail is empty.- Pick the DNS provider and its variables together (DNS-01 only).
traefik_dns_provideris a lego provider code, andtraefik_dns_envholds that provider’s variable names. See the lego documentation for them. Keep the values in a vault. - Test with the ACME staging directory first. Let’s Encrypt rate-limits failed validations on the production directory.
traefik_network_rangemust not containtraefik_address. The fixed address is deliberately outside the pool Docker assigns from.- Access logs go to stdout. For CrowdSec to read them they must be written to a file on the host; see crowdsec.
- The MTU of the compose networks is
traefik_mtu. Match it todocker_mtuon networks that need less than 1500. error-pages.ymlis reserved.traefik_dynamic_filescannot contain a file of that name while error pages are enabled.