Guide
Traefik fundamentals explained
You deploy a new API container on Friday afternoon. With
nginx, that means editing
server blocks, running nginx -t, reloading, and hoping you did not
typo a hostname. With Traefik, you add a few Docker labels — or a
Kubernetes IngressRoute — and the edge proxy discovers the service, wires TLS, and
starts routing within seconds. Traefik is a cloud-native reverse proxy and load balancer
built for dynamic environments:
Docker Compose stacks,
Kubernetes clusters,
and Nomad jobs where backends appear and vanish without human intervention. This guide
covers Traefik’s static vs dynamic configuration split, the entrypoint-router-service-middleware
model, provider integrations, automatic
HTTPS via ACME, load balancing
and health checks, a Harbor Fleet microservices worked example, a reverse-proxy decision
table, common pitfalls, and a production checklist.
What Traefik is and when to choose it
Traefik sits at the edge of your infrastructure: it terminates TLS, routes HTTP requests to the correct backend, applies middleware (rate limits, auth, path rewrites), and exposes a dashboard for live configuration. Unlike file-driven proxies that require reloads, Traefik watches providers — Docker socket events, Kubernetes API watches, Consul, etc. — and hot-reloads its routing table.
Static vs dynamic configuration
Traefik splits config into two layers. Static configuration
(traefik.yml or CLI flags) defines immovable settings: which entrypoints
listen on which ports, which providers are enabled, ACME email and storage, logging
levels, and the dashboard bind address. Dynamic configuration comes
from providers and defines routers, services, and middlewares — the parts that
change when you scale a deployment. This separation means you rarely touch static
config after initial bootstrap; day-to-day routing lives in labels or CRDs.
Four core object types
- Entrypoints — network listeners (e.g.
:80,:443). - Routers — match Host, Path, Headers; attach middlewares; point to a service.
- Services — load-balancer definitions over one or more backend URLs.
- Middlewares — reusable request/response transforms (strip prefix, basic auth, rate limit).
Request flow: client hits an entrypoint → router rules match → middleware chain runs → service forwards to backend pool. Understanding this pipeline is the key to debugging 404s and certificate issues.
Docker provider: label-based routing
The most common Traefik setup runs as a container with access to the Docker socket. Other containers opt in with labels Traefik reads at runtime. A minimal pattern:
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.example.com`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.services.api.loadbalancer.server.port=8080"
traefik.enable=true is required when Traefik is configured to ignore
unlabeled containers (recommended in shared Docker hosts). The router rule uses Traefik’s
rule syntax: Host(), PathPrefix(), Headers(), and
boolean combinators && / ||. The service port label tells
Traefik which container port to target when the image exposes multiple ports.
Compose networking
Traefik and backends must share a Docker network. A dedicated proxy network
connects Traefik to every routable service; internal databases stay on a private network
Traefik cannot reach. Never publish database ports to the host when Traefik handles
public ingress — only Traefik needs host port bindings (80,
443).
Middleware via labels
Define middleware once on Traefik or attach inline. Example: strip /api
before forwarding:
- "traefik.http.middlewares.api-strip.stripprefix.prefixes=/api"
- "traefik.http.routers.api.middlewares=api-strip"
Middlewares are composable — chain rate limiting, compression, and forward-auth in one router. Reuse the same middleware name across routers for consistent policy.
Kubernetes provider and IngressRoute
In Kubernetes, Traefik replaces or complements the stock Ingress controller. Modern
setups use IngressRoute CRDs (Traefik’s native resource) or
standard Ingress objects with Traefik annotations. IngressRoute supports
TCP/UDP routing, middleware references by Kubernetes name, and weighted traffic
splitting — features the vanilla Ingress spec lacks.
Install Traefik via Helm with providers.kubernetesCRD and
providers.kubernetesIngress enabled. Each namespace can define routes;
RBAC grants Traefik read access to Services and Endpoints. When a Deployment scales,
Traefik’s endpoint list updates without manual intervention — the core value
proposition over static nginx ConfigMaps that require a rolling reload on every change.
Gateway API trajectory
Traefik supports the Kubernetes Gateway API for teams standardizing on upstream CRDs. Migration path: start with IngressRoute for Traefik-specific middleware, evaluate Gateway API when multi-controller portability matters. Either way, TLS secrets or cert-manager integrate the same as with nginx Ingress.
Automatic HTTPS with ACME
Traefik’s built-in certificates resolvers obtain and renew
Let’s Encrypt certificates automatically. Static config declares a resolver
(e.g. letsencrypt) with ACME endpoint, storage path (JSON file or
Kubernetes secret), and contact email. Routers reference
tls.certresolver=letsencrypt to trigger issuance on first request.
HTTP-01 vs TLS-ALPN-01
HTTP-01 requires port 80 reachable from the internet; Traefik serves
the challenge on the web entrypoint. TLS-ALPN-01 works
when only 443 is exposed — common in locked-down environments. Pick one challenge
type per resolver; mixing incorrectly produces endless renewal failures in logs.
Wildcard certificates
Wildcard certs require DNS-01 challenges via provider plugins (Route53, Cloudflare,
etc.). HTTP-01 cannot validate *.example.com. Store ACME account keys and
issued certs in persistent volume or secret — ephemeral containers that lose
acme.json on restart will re-request certs and hit Let’s Encrypt
rate limits quickly.
Load balancing, health checks, and observability
Load balancing strategies
Services default to round-robin across healthy backends. Traefik supports weighted round-robin for canary deploys (90% v1, 10% v2), sticky sessions via cookie, and passive health checks that temporarily remove failing servers. For gRPC and WebSocket, ensure the service protocol is set correctly — Traefik handles connection upgrades when backends support them.
Health checks
Active health checks ping a backend path on an interval; unhealthy targets are removed
from rotation. Configure healthcheck.path, interval, and
timeout on the service. Without health checks, Traefik keeps sending traffic
to crashed containers until Docker removes them from endpoint lists — a gap that
causes 502 bursts during rolling updates.
Metrics and tracing
Enable Prometheus metrics on a dedicated entrypoint for request rates, status codes, and backend latency histograms. OpenTelemetry export integrates with observability stacks. Access logs with default fields include router name and service name — essential for correlating edge behavior with application logs downstream.
Worked example: Harbor Fleet microservices on Docker Compose
Harbor Fleet runs three APIs — status, billing, and notifications — plus a static dashboard. Traefik replaces manual nginx config:
- Traefik container — mounts
/var/run/docker.sock, exposes 80/443, persistsacme.jsonon a named volume. - Static config — entrypoints
web(80) redirects towebsecure(443); cert resolverletsencryptwith HTTP-01 onweb. - status-api — labels route
status.harbor-fleet.exampleto port 3001; middleware adds security headers. - billing-api —
api.harbor-fleet.examplewithPathPrefix(/billing); strip-prefix middleware rewrites to backend root. - notifications — WebSocket route on
ws.harbor-fleet.example; service protocol left as HTTP with upgrade support. - dashboard — static files served by a tiny nginx sidecar OR Traefik file provider for assets; router on apex domain.
- Deploy —
docker compose up -d billing-apiadds routes automatically; no Traefik restart.
When billing-api scales to three replicas, Traefik load-balances across all healthy containers sharing the same router labels. Canary deploys add a second service with weighted labels before decommissioning the old image tag.
Decision table: Traefik vs nginx vs Caddy vs cloud LB
| Scenario | Prefer | Avoid |
|---|---|---|
| Single VPS, static files + one Node API | nginx | Traefik (Docker socket overhead for one process) |
| Docker Compose with 5+ services that change often | Traefik | Hand-edited nginx upstream blocks |
| Kubernetes with frequent Deployments | Traefik or nginx Ingress | NodePort per service without ingress |
| Automatic HTTPS, minimal config, small project | Caddy | Full Traefik dashboard + CRD stack for one site |
| High-throughput static CDN origin | nginx | Traefik as pure file server (not its strength) |
| Multi-tenant SaaS with per-customer hostnames | Traefik + Docker/K8s labels | One giant nginx config file per tenant |
| Managed cloud (AWS ALB, GCP LB) | Cloud LB + Traefik/nginx behind it | Self-managing certs at both layers without reason |
Common pitfalls
- Missing
traefik.enable=true— container runs but never receives traffic when exposedByDefault is false. - Wrong network — Traefik and backend on different Docker networks; connection refused despite correct labels.
- Ephemeral
acme.json— cert loss on restart triggers rate limits; mount persistent storage withchmod 600. - HTTP-01 blocked — Cloudflare orange-cloud or firewall blocks port 80; switch to DNS-01 or TLS-ALPN-01.
- Duplicate router names — Colliding router names across containers cause unpredictable overrides.
- StripPrefix without backend awareness — App generates wrong URLs when it does not know about path rewriting.
- Docker socket exposure — Traefik needs socket access; compromise equals cluster admin. Restrict who can label containers.
- No resource limits on Traefik — Edge proxy OOM during traffic spikes takes down every route at once.
Production checklist
- Static config in version control; dynamic routes defined via labels or GitOps CRDs.
exposedByDefault=false; only labeled services are routable.- Persistent ACME storage with correct permissions; renewal tested before expiry window.
- HTTP → HTTPS redirect middleware on port 80 entrypoint.
- Health checks on every service with more than one replica or during rolling deploys.
- Security headers middleware (HSTS, frame deny, content-type nosniff) on public routers.
- Rate limiting on auth and expensive endpoints via middleware.
- Dashboard bound to internal network or protected with basic auth — never public unauthenticated.
- Prometheus metrics scraped; access logs shipped to aggregation.
- Docker socket or K8s RBAC scoped to minimum privilege; audit who can deploy labeled containers.
- Staging resolver using Let’s Encrypt staging CA before production cutover.
- Document label conventions (router naming, network membership) in team runbook.
Key takeaways
- Traefik excels when backends are dynamic — containers and pods that scale without manual proxy edits.
- Config splits static (entrypoints, providers, ACME) and dynamic (routers, services, middlewares).
- Docker labels and Kubernetes CRDs are the primary routing interfaces in cloud-native stacks.
- Built-in ACME automates TLS issuance and renewal — persist cert storage.
- For a single static VPS, nginx may be simpler; for multi-service Compose or K8s, Traefik pays for its learning curve.
Related reading
- nginx fundamentals explained — event-driven static and reverse-proxy patterns on a single VPS
- Docker fundamentals explained — images, networks, and Compose orchestration Traefik routes over
- Kubernetes fundamentals explained — pods, services, and ingress concepts behind Traefik CRDs
- Load balancing explained — L4 vs L7 algorithms, health checks, and sticky sessions