Traefik: The Reverse Proxy That Configures Itself While You Sleep
"Nginx makes you describe your infrastructure. Traefik goes and looks at it — and updates itself the moment it changes."
If you self-host more than three services, you have a reverse proxy problem. Every app wants its own port, every one needs TLS, and every new container means another hand-edited config block and another reload.
Traefik (GitHub:
traefik/traefik) eliminates that toil with one idea: instead of you describing your services to the proxy, the proxy watches your platform and configures itself. As of August 2026 it carries roughly
64,000 stars, ships under
MIT, runs as a single Go binary, and is at
v3.7.12 (released August 26, 2026). With over 3.4 billion Docker Hub downloads and CNCF project status, it has become — especially as Ingress NGINX retires — the default answer for container-native routing. For a blog about software you run on your own hardware, Traefik is the front door: the one piece of infrastructure every service you self-host sits behind.
This is the honest breakdown — how the discovery model works, why zero-restart reloads matter more than they sound, what the paid tiers actually gate, and the two things (configuration model and debugging) that most often make people quit.
1. What Traefik Is (and Isn't)
Traefik is a
reverse proxy and load balancer — an edge router that sits in front of your services and sends incoming requests to the right place. It also terminates TLS, applies middleware, load-balances, and can route TCP and UDP, not just HTTP.
Its distinguishing trait is
dynamic configuration sourced from service discovery. Point it at the Docker socket or the Kubernetes API and it watches for services appearing and disappearing, then rebuilds its routing table in real time. You express routing intent as
labels on the container rather than in a central config file:
``
yaml
services:
whoami:
image: traefik/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(whoami.example.com
)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
`
Start that container and Traefik routes to it. Stop it and the route disappears. No restart, no reload, no edit.
What it is not is a static-file web server. If your primary workload is serving high-volume static assets, Nginx will beat it. Traefik is built for dynamic, containerised, service-oriented infrastructure — it's a router, not an origin server.
2. The Core Idea: Configuration Comes From the Platform
Traditional proxies invert the relationship: you maintain a file listing your upstreams, and the proxy believes the file. That's fine with five services and miserable with fifty, because the file drifts from reality the moment anything changes.
Traefik flips it. Providers (Docker, Kubernetes, Consul, Etcd, Nomad, ECS, Rancher, or plain file/HTTP) each watch their source and emit a complete configuration snapshot whenever state changes. A throttling layer prevents "configuration stampedes" when many containers start at once. Then Traefik atomically swaps in the new router tree — existing connections keep running, no requests are dropped, and nothing restarts.
The practical consequence for a self-hoster: adding a service is a Compose file change, not an infrastructure change. Over a year of tinkering, that difference compounds into hours you don't spend editing proxy configs.
The trade-off is that the "label and provider" model is where the learning curve lives. You're not writing a config file, you're writing metadata that generates a config, and understanding the object model — entrypoints, routers, services, middlewares — is the price of admission.
3. Why Zero-Restart Reloads Matter
This sounds like a minor operational nicety. It isn't, for two reasons.
No dropped requests. When Nginx reloads, it signals a process and briefly re-establishes things. Traefik swaps the routing table in place. For a home server hosting things other people use — a family photo share, a status page, a game server — "the proxy reloaded" stops being a maintenance window.
It makes automation safe. Because configuration is derived from live state, you can let CI, Compose, or a scheduler create and destroy services freely without coordinating proxy updates. That's what makes Traefik viable in Kubernetes, where pods come and go constantly, and it's equally valuable on a single Docker host where you're constantly trying new containers.
4. Automatic HTTPS: The Feature That Sells It
If service discovery is the clever part, automatic TLS is the part that converts people.
Traefik integrates go-acme/lego
— the same ACME library behind Caddy's auto-TLS — and will obtain and renew Let's Encrypt certificates automatically. It supports HTTP-01, TLS-ALPN-01, and DNS-01 challenges across 100+ DNS providers, which means wildcard certificates work too. Certificates renew before expiry with no cron job and no 3 a.m. "certificate expired" page.
For anyone who has hand-rolled certbot renewals, this alone justifies the migration. And because Traefik terminates TLS centrally, every service behind it gets HTTPS without any of them knowing about certificates — which is exactly the right separation of concerns for a homelab where half the services are containers you didn't write.
5. Middleware: Where the Real Work Happens
Routing is table stakes. The middleware pipeline is where Traefik earns its place, and it's more capable than people expect:
- Rate limiting — protect services from abuse and brute force.
- Circuit breaker and retry — fail gracefully instead of cascading.
- Basic auth / forward auth — put a login in front of anything, including apps that have none.
- Compression — gzip, zstd, and Brotli are built in, not bolted on.
- Headers — security headers, CORS, HSTS, applied uniformly at the edge.
- IP allowlisting — restrict sensitive services to your tailnet.
That last pair matters enormously for self-hosters. Many excellent self-hosted apps ship with no authentication at all — Meilisearch and changedetection.io both default to an open UI. Putting forward-auth or IP allowlisting at the proxy means you can run them without exposing an unauthenticated dashboard to the internet. Centralising that concern at the edge is the single biggest security upgrade available to a homelab.
6. The Cost, Honestly
Software: $0 for Traefik Proxy under MIT — no licence cost, no copyleft obligation, and the open-source edition includes no telemetry.
Hosting: nearly free. It's a stateless single binary; it'll run happily alongside your other containers on hardware you already own. If you're buying a VPS for it, $4–6/month is plenty.
What you'd pay for: the commercial tiers (Traefik Enterprise / Hub) gate high-availability clustering, distributed configuration, RBAC, the native WAF, OIDC/LDAP access control, and multi-cluster management. If you need those, budget for them.
The common open-source workaround: the native WAF is paid, but a widespread pattern is pairing the MIT proxy with CrowdSec for behavioural filtering and IP reputation — keeping the security layer open source too. That combination covers most self-hosted threat models without a licence.
Your time: moderate. The concepts are learnable in an afternoon; the first "why isn't this route matching" session is inevitable.
7. Honest Limitations
- Enterprise features are gated. WAF, OIDC/LDAP SSO, HA clustering, and multi-cluster management sit behind paid tiers. If you need centralised SSO at the proxy, that's a purchase or a separate tool (like Authentik).
- The configuration model has a learning curve. Entrypoints, routers, services, middlewares, providers, and the label syntax — it's a real object model, and reading someone else's Compose labels can be harder than reading an Nginx config.
- Debugging routing is the weak spot. Because behaviour emerges from layered middleware and provider abstraction, "why is this 404ing" can mean tracing through several layers. The dashboard helps; the error messages sometimes don't.
- Static content isn't its strength. Nginx is faster at serving high-volume static files.
- Open-core roadmap risk. A single commercial steward decides what stays in the MIT core and what moves to a paid tier. Nothing critical has been pulled, but it's a governance reality worth knowing.
- Mixing static and dynamic config is where most misconfigurations happen — pick one mental model and be consistent.
8. Getting Started
`
yaml
services:
traefik:
image: traefik:v3.7
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "[email protected]"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
restart: unless-stopped
`
Three notes that save pain: set exposedbydefault=false
so containers must opt in with traefik.enable=true
(otherwise Traefik will happily publish everything); make sure acme.json
has restrictive permissions (600) or Traefik will refuse to use it; and enable the dashboard only on an internal entrypoint, never publicly.
9. Traefik vs the Alternatives
| | Traefik | Nginx | Caddy | HAProxy |
|---|---|---|---|---|
| Auto service discovery | ✅ | ❌ manual | partial | ❌ |
| Auto TLS | ✅ built in | via certbot | ✅ automatic | via certbot |
| Config style | labels / CRDs | static file | Caddyfile | static file |
| Static file serving | adequate | excellent | good | limited |
| Learning curve | moderate | low | low | high |
| License | MIT | BSD-2 | Apache-2.0 | GPL-2.0 |
| Best for | containers / K8s | traditional stacks | simplicity | raw performance |
The short version: Traefik wins when your services are containers that change often. Caddy wins when you want the simplest path to automatic HTTPS with a five-line config. Nginx wins for traditional, static, high-volume serving and has the longest track record. HAProxy wins on raw throughput and fine-grained control.
10. Who Should Run It
Run it if: you run more than a handful of Docker containers, you're tired of hand-managing certificates, you want centralised auth in front of apps that have none, or you're on Kubernetes and need an ingress controller.
Skip it if: you have two services and a static Nginx config you never touch (just keep Nginx), your workload is mostly static files, or you want the absolute simplest config syntax (Caddy is friendlier for beginners).
For this blog's readers — running Home Assistant, SearXNG, Meilisearch, changedetection.io, and a dozen other containers — Traefik is the piece that turns a pile of ports into a coherent set of hostnames with real certificates, and puts one authentication layer in front of all of it.
11. A Real Deployment Walkthrough
Step 1 — opt-in, not opt-out. Start with exposedbydefault=false
. You want to publish deliberately, not discover later that Traefik exposed your database.
Step 2 — get one service working end to end. Pick the simplest possible container, add its labels, and confirm you can reach it by hostname over HTTPS. Don't migrate everything at once.
Step 3 — secure the dashboard. Enable it on an internal entrypoint or behind auth. A public Traefik dashboard advertises your entire service topology to anyone who finds it.
Step 4 — add forward auth. This is the highest-value middleware for a homelab: it lets you put a login in front of every unauthenticated app at once.
Step 5 — then migrate the rest. Move services over one at a time, verifying each. The atomic reload means you never need a maintenance window.
12. Troubleshooting
- 404 with no explanation — usually the router rule doesn't match (check
Host()
against the actual hostname) or the container isn't on the same Docker network as Traefik. Network mismatch is the most common cause.
Certificate not issued — HTTP-01 needs port 80 reachable from the internet. If that's impossible, switch to DNS-01.
acme.json
permissions error — the file must be 600. Traefik refuses otherwise, deliberately.
Middleware doing nothing — middlewares must be attached to a router and referenced by name correctly (traefik.http.routers.X.middlewares=name@docker
). The @docker
suffix trips people up constantly.
Everything exposed unexpectedly — you left exposedbydefault
on. Turn it off.
Config changes not applying — check you're editing the right provider source; file-provider changes need the file watcher enabled.
13. Plugins and Extensibility
One underrated architectural detail: Traefik's plugin system runs middleware as WebAssembly modules. That means community middleware written in any WASM-compilable language can be loaded at startup without forking or recompiling Traefik itself. It's a clean extension model, and it's why a plugin ecosystem exists without the core becoming bloated.
The obvious caution: plugins are third-party code running inside your edge proxy — the single most security-sensitive component you operate. Treat plugin selection like dependency selection: prefer well-maintained ones, read what they do, and remember that a compromised or sloppy plugin sits in the path of every request.
14. Security Posture
A few structural facts worth internalising, because Traefik is your perimeter:
- It's stateless and stores no user data, so vendor jurisdiction is largely irrelevant to your traffic — it runs entirely inside your network.
- It routes traffic, it doesn't store it, which means there's no vendor compliance certification to inherit. TLS configuration, host hardening, and audit logs are your responsibility.
- Mounting the Docker socket read-only (
:ro
) is the standard hardening step, but be aware that Docker socket access is effectively root-equivalent on the host. If that threat model bothers you, use a socket proxy that exposes only the read endpoints Traefik needs.
The dashboard is information disclosure. Never expose it publicly.
Handled properly, Traefik is production-grade at the edge. Handled casually, it's the most exposed thing you run. The difference is entirely in these details.
15. The Object Model, Plainly
Most Traefik confusion dissolves once the four core objects are clear, so here they are without jargon:
- Entrypoints — the ports Traefik listens on.
web
(80), websecure
(443), maybe internal
for things you never want public. This is where traffic enters.
Routers — the rules that match incoming requests. Host(
whoami.example.com)
is a router rule. This decides which requests go where.
Services — the backends traffic is sent to. Usually auto-discovered from a container, but you can define them manually. This is where the request ends up.
Middlewares — transformations applied in between: auth, rate limits, headers, compression, path rewriting. This is what happens on the way.
The mental model is a pipeline: entrypoint → router (match) → middleware chain → service. Almost every Traefik question is answerable by asking which of those four links is misbehaving.
Two syntax notes that cause disproportionate pain: middlewares must be attached to a router (traefik.http.routers.X.middlewares=name@docker
), and the @docker
suffix matters when the middleware is defined in a provider rather than in static config. Forgetting it is the single most common "my middleware does nothing" cause.
16. Traefik in a Homelab: A Practical Pattern
The pattern that works well for a self-hosted estate, and which avoids the most common security mistake:
Separate internal from external. Define two entrypoints. websecure
(443) for services you genuinely want reachable from the internet, and an internal one for everything else — databases, dashboards, admin UIs. Most of your services belong on the internal one.
Default to not exposing. Keep exposedbydefault=false` so a stray container never becomes public by accident. Opt in explicitly, service by service.
Put auth at the edge once. Rather than configuring logins in fifteen different apps, use a single forward-auth middleware. Apps with no built-in authentication — and there are more of them than you'd think — become safe without you patching any of them.
Combine with a private network. If you run headscale or another mesh VPN, plenty of services don't need to be on the public internet at all. Traefik then exists mainly to give them hostnames and certificates rather than to expose them.
Handled this way, Traefik stops being "the thing that might expose my database" and becomes what it should be: one clean front door with a lock you chose.
17. Observability: Seeing What the Proxy Sees
Because every request passes through it, Traefik is the best place in your stack to observe traffic — and it ships the plumbing for that for free.
It can expose
Prometheus metrics, emit access logs, and send traces through OpenTelemetry. Point a monitoring stack at the metrics endpoint and you get request rates, status-code distributions, and per-service latency for every service you run, without instrumenting any of them individually. If you already run Uptime Kuma for availability (see Related), adding Traefik's metrics gives you the other half of the picture: not just "is it up" but "is it slow, and for whom."
Access logs are equally useful for the mundane questions — which client keeps hitting that 404, whether a scanner is probing your edge, whether a renewal actually succeeded. Enable them, rotate them, and you'll be glad they exist the first time something behaves oddly.
One caution: access logs contain request metadata, which can include URLs with sensitive query parameters. Treat them as logs to protect, not as debug output to leave on an unprotected volume.
Related
Comments (0)
No comments yet. Be the first to comment!