Tech Digest

Networking

Traefik

Reverse proxy that builds its routing table from Docker labels, with ACME built in

NetworkingMITMaturearm64 builds
Last reviewed Profile maintained against the project's own documentation
Operational load
3 / 5
Moderate
Idle memory
60 MB
typical use ~130 MB
Moving parts
1
containers in a normal deployment
Backup shape
File copy
live file copy safe

Traefik is a Go reverse proxy and edge router that discovers backends from providers (Docker, Kubernetes, files, Consul) instead of from a static config file. It replaces a hand-maintained nginx config in environments where containers are created and destroyed regularly.

Our verdict on Traefik#

Traefik earns its place when containers come and go: you add traefik.enable=true plus a router label and the route exists a second later with a certificate. Nothing file-based matches that. The cost is that the internet is full of v2 snippets and Traefik does not tell you when you paste one. On v3 (v3.7.13 as of early September 2026) rule values go in backticks and combine with &&, not commas; Headers became Header, HostHeader was removed in favour of Host, PathPrefix no longer accepts {placeholder} segments (use PathRegexp), and IPWhiteList is now IPAllowList. The headers middleware lost sslRedirect, sslHost and featurePolicy outright. A stale label does not error, it just fails to match, and you get a 404 from whichever router did match. The per-router ruleSyntax=v2 escape hatch works but has been deprecated since v3.4 and goes away in the next major.

Who Traefik is for#

Choose it if

  • A compose host with a dozen stacks where you add and remove services weekly and do not want to touch proxy config
  • Anyone who needs wildcard certificates via DNS-01 across many subdomains, since the Lego provider list is broad and configured with plain environment variables
  • Teams already running Kubernetes who want the same ingress controller on their laptop and in the cluster
  • Setups that need middleware chains (rate limit, ForwardAuth to Authelia, IP allow list) expressed declaratively per service

Look elsewhere if

  • You want a highly available pair of proxies with Let's Encrypt; the file-based ACME store explicitly cannot be shared between instances
  • You are uncomfortable giving a container read access to the Docker socket and do not want to run a socket proxy
  • Your backends are static VMs, not containers, in which case a Caddyfile or plain nginx is less machinery for the same result

What running it actually looks like#

One container. The Docker provider needs read access to /var/run/docker.sock, which lets Traefik enumerate every container on the host; mount it read-only and consider a socket proxy if that is uncomfortable. Config splits in two and the split is the thing people miss: static config (entrypoints, providers, certificate resolvers) lives in traefik.yml or CLI flags and only takes effect on restart, while dynamic config (routers, services, middlewares) comes from labels or watched files and hot reloads. Publish 80 and 443. The dashboard on 8080 only appears if you set --api.insecure=true or route it through Traefik itself with auth. Create acme.json as an empty file and chmod 600 it before the first start. First run with a wrong label produces no error at all: you get a 404 and have to open the dashboard's routers view to see that nothing matched.

Resource profile#

Memory
60 MB idle, around 130 MB in ordinary use. Go binary; memory tracks the number of routers, services and watched containers plus connection buffers, not traffic volume.
CPU and acceleration
Near zero when proxying. Spikes come from provider churn: a compose stack restarting causes a full config rebuild across every watched container.
Storage growth
acme.json is a few KB per certificate. Access logs, if enabled, are the only growth and need rotation you configure yourself.
Operational load
3 of 5, Moderate. Single container and no database, but the config surface has moved twice in the project's life, label syntax errors fail silently, and the ACME file store forces you to run exactly one instance. Upgrade notes are worth reading every time.

Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.

Data and backup surface#

Traefik holds almost no state. Back up the static config file, any dynamic file-provider directory, and acme.json. That one file contains the ACME account private key plus every issued certificate and its private key as a single JSON document, and Traefik refuses to use it unless the mode is exactly 600. It is small (a few KB per certificate) and a copy while running is fine. Everything else is derived: routers, services and middlewares are rebuilt from Docker labels or files on every start, so there is nothing to dump. Access logs and the Prometheus endpoint are regenerable and should be excluded. Restore is: put the files back, chmod 600 acme.json, start.

Derived backup shape

File copy. Copy the config and data directories. Safe while running for anything that writes atomically.

Traps and surprises#

Copy-pasted v2 labels 404 silently on v3

v3 requires backticks around matcher values and && between matchers, renamed Headers to Header and IPWhiteList to IPAllowList, removed HostHeader, and dropped {placeholder} support from Path/PathPrefix in favour of PathRegexp. Traefik does not reject the old form loudly; the router simply never matches. traefik.http.routers.x.ruleSyntax=v2 buys you time but is deprecated as of v3.4 and scheduled for removal in the next major.

Docker creates a directory where you wanted acme.json

If you bind mount ./acme.json:/acme.json and the host path does not exist, Docker helpfully creates a directory with that name. Traefik then fails to persist certificates and re-requests them on every restart until Let's Encrypt rate limits you. Run touch acme.json && chmod 600 acme.json first. The 600 requirement is enforced: a group- or world-readable file is ignored with a warning most people never read.

One Traefik per acme.json, no exceptions

The docs state plainly that you cannot run multiple Traefik instances with Let's Encrypt enabled, because there is no coordination over which instance receives a given challenge. Two replicas on a shared volume will each write the file and clobber each other. If you need HA, the documented answers are cert-manager or the commercial distributed store, not a shared NFS mount.

The Docker socket is root on the host

Read access to the socket lets a compromised Traefik enumerate every container, its environment variables and its mounts. Write access lets it start a privileged container and own the machine. Mount it :ro at minimum, and prefer a socket proxy that whitelists only the container and event endpoints Traefik actually uses. This is the standard Traefik setup and it is also the standard Traefik risk.

Editing traefik.yml does nothing until restart

Adding an entrypoint, changing a certificate resolver or turning on a new provider are all static configuration and require the process to restart. Only routers, middlewares and services reload live. People add entryPoints.websecure to the YAML, see no error and no effect, and conclude Traefik is broken. docker compose up -d --force-recreate traefik is the fix.

Specifications#

Category
Networking, DNS and remote access
Licence
MIT (Permissive)
Written in
Go
First release
2015
Maturity
Mature
Datastore
None. Certificates in a single acme.json file; routing state rebuilt from providers at every start
Default ports
80, 443, 8080
Ships as
docker, kubernetes helm chart, native binary
arm64 builds
Yes
Identity
Auth proxy only. No user accounts. The dashboard is unauthenticated unless you put a BasicAuth or ForwardAuth middleware in front of it, which is also how you bolt Authelia onto backends.
Replaces
Nginx Plus, AWS Application Load Balancer, HAProxy Enterprise, Cloudflare Tunnel
Project site
traefik.io
Source
github.com/traefik/traefik
Documentation
doc.traefik.io/traefik

Alternatives to Traefik#

Everything else in networking, dns and remote access, closest in operational weight first.

ToolOps loadIdle RAMLicence
Headscale3, Moderate40 MBBSD-3-Clause
Technitium DNS Server3, Moderate130 MBGPL-3.0
AdGuard Home2, Light60 MBGPL-3.0
Nginx Proxy Manager2, Light100 MBMIT
Pi-hole2, Light100 MBEUPL-1.2
wg-easy2, Light90 MBAGPL-3.0-only

Where Traefik comes up elsewhere#

Sources#

Facts on this page were checked against the project's own material. Where a number is our own estimate rather than a published figure, the text says so.

  1. Official migration guide, v2 to v3 configuration details
  2. Official migration guide, v2 to v3 overview and ruleSyntax
  3. Official documentation, ACME certificate resolvers
  4. GitHub releases
  5. Licence file

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.