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:

  1. Traefik container — mounts /var/run/docker.sock, exposes 80/443, persists acme.json on a named volume.
  2. Static config — entrypoints web (80) redirects to websecure (443); cert resolver letsencrypt with HTTP-01 on web.
  3. status-api — labels route status.harbor-fleet.example to port 3001; middleware adds security headers.
  4. billing-apiapi.harbor-fleet.example with PathPrefix(/billing); strip-prefix middleware rewrites to backend root.
  5. notifications — WebSocket route on ws.harbor-fleet.example; service protocol left as HTTP with upgrade support.
  6. dashboard — static files served by a tiny nginx sidecar OR Traefik file provider for assets; router on apex domain.
  7. Deploydocker compose up -d billing-api adds 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 with chmod 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