Traefik 3.0: Modern Reverse Proxy Guide
Traefik 3.0 has arrived as the most flexible, cloud‑native reverse proxy you can run today. Whether you’re orchestrating containers with Docker, deploying services on Kubernetes, or managing a hybrid stack, Traefik’s declarative configuration and built‑in observability make it a natural fit. In this guide we’ll walk through the core concepts, show you how to get up and running in minutes, and dive into advanced patterns that power production‑grade environments.
What’s new in Traefik 3.0?
Version 3.0 introduces a revamped router engine, first‑class support for HTTP/3, and a more granular middleware pipeline. The new Provider API lets you reload dynamic configuration without touching the static file, dramatically reducing downtime. Additionally, the built‑in Metrics Dashboard now ships with Prometheus, Grafana, and OpenTelemetry exporters out of the box.
Another noteworthy change is the simplification of TLS handling. Let’s Encrypt certificates are now automatically renewed using the tlsStore abstraction, and you can mix multiple certificate resolvers on a per‑router basis. If you’ve used Traefik 2.x before, most of the migration steps are straightforward, but we’ll highlight the key differences to avoid surprises.
Core concepts refresher
- Static configuration – defines entrypoints, providers, and global options. Loaded once at startup.
- Dynamic configuration – describes routers, services, and middlewares. Can be hot‑reloaded.
- Providers – sources of dynamic config (Docker, Kubernetes, file, etc.).
- Middlewares – reusable request/response transformations (auth, rate‑limit, headers).
Understanding how these pieces fit together is essential before we dive into code. Think of the static file as the skeleton, the providers as the nervous system feeding data, and the dynamic objects as the muscles that move traffic.
Installing Traefik 3.0
The quickest way to try Traefik is via Docker. Pull the official image, mount a static configuration file, and expose the entrypoints you need. Below is a minimal docker-compose.yml that launches Traefik with HTTP (port 80) and HTTPS (port 443) entrypoints.
version: "3.9"
services:
traefik:
image: traefik:v3.0
command:
- "--api.insecure=true" # Dashboard on :8080
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=admin@example.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt"
restart: unless-stopped
Save the file as docker-compose.yml and run docker compose up -d. Traefik will automatically discover any containers that expose ports and have the appropriate labels.
Pro tip: Keep the acme.json file on a persistent volume. Losing it forces Traefik to re‑issue certificates, which can trigger rate‑limit blocks from Let’s Encrypt.
Static vs. dynamic files
While the Docker provider covers many use cases, you might still need a static file for complex setups (e.g., custom entrypoints or TLS stores). Create a traefik.yml alongside the compose file and reference it with --configFile=/etc/traefik/traefik.yml. Here’s a concise example:
# traefik.yml
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
http:
tls:
certResolver: myresolver
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
api:
dashboard: true
Notice the exposedByDefault: false flag – it forces you to explicitly opt‑in each container, improving security in multi‑tenant environments.
Defining routers, services, and middlewares
Dynamic configuration can be expressed in YAML or TOML. Below we define a simple router that forwards example.com traffic to a backend service named whoami, while applying a header‑adding middleware.
# dynamic.yml
http:
routers:
whoami-router:
rule: "Host(`example.com`)"
entryPoints:
- websecure
service: whoami-service
middlewares:
- add-security-headers
tls:
certResolver: myresolver
services:
whoami-service:
loadBalancer:
servers:
- url: "http://whoami:80"
middlewares:
add-security-headers:
headers:
customResponseHeaders:
X-Content-Type-Options: "nosniff"
X-Frame-Options: "DENY"
Mount this file into the container with - "./dynamic.yml:/etc/traefik/dynamic.yml" and add --providers.file.filename=/etc/traefik/dynamic.yml to the command list. Any change to dynamic.yml triggers an instant reload – no container restart needed.
Combining multiple providers
- Docker for rapid development.
- File provider for static, version‑controlled routes.
- Kubernetes CRDs for cluster‑wide ingress.
Traefik will merge configurations from all active providers, respecting priority rules (Docker > File > Kubernetes). This allows you to keep a core set of routes in Git while letting developers spin up temporary services on the fly.
TLS, Let's Encrypt, and HTTP/3
Traefik 3.0 ships with native HTTP/3 support, which you can enable per entrypoint. Add the following to your static file:
entryPoints:
websecure:
address: ":443"
http:
tls:
certResolver: myresolver
http3:
advertisedPort: 443
With HTTP/3 enabled, browsers that support QUIC will automatically negotiate a faster, UDP‑based connection. Remember that firewalls must allow inbound UDP on the advertised port.
Pro tip: Use a dedicated tlsStore for internal services that require self‑signed certificates. This isolates them from public Let’s Encrypt resolvers and prevents accidental exposure.
Multiple certificate resolvers
If you need both a wildcard certificate for *.example.com and a single‑domain cert for admin.example.com, define two resolvers and assign them per router:
certificatesResolvers:
wildcard:
acme:
email: admin@example.com
storage: /letsencrypt/wildcard.json
dnsChallenge:
provider: cloudflare
delayBeforeCheck: 0
single:
acme:
email: admin@example.com
storage: /letsencrypt/single.json
tlsChallenge: {}
# Router example
http:
routers:
admin-router:
rule: "Host(`admin.example.com`)"
entryPoints:
- websecure
service: admin-service
tls:
certResolver: single
This approach keeps renewal scopes separate, which is handy when you have different DNS providers or rate‑limit concerns.
Middlewares in depth
Middlewares are the real power‑houses of Traefik. They let you inject authentication, rate limiting, redirection, and even custom plugins without touching your application code. Below are three common patterns.
Rate limiting
http:
middlewares:
api-rate-limit:
rateLimit:
average: 100
burst: 20
period: "1m"
Attach api-rate-limit to any router handling public APIs. The configuration caps requests to 100 per minute on average, with a burst of 20 for short spikes.
Basic authentication
http:
middlewares:
admin-auth:
basicAuth:
users:
- "admin:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
Generate the hashed password with htpasswd -nb admin password | openssl base64. This middleware is perfect for protecting staging dashboards or internal tools.
Redirects and path stripping
http:
middlewares:
strip-www:
redirectRegex:
regex: "^https?://www\\.(.*)"
replacement: "https://$${1}"
permanent: true
strip-api-prefix:
stripPrefix:
prefixes:
- "/api"
Combine them in a router to enforce a canonical domain and expose clean API endpoints.
Monitoring, logs, and observability
Traefik integrates seamlessly with Prometheus, Grafana, and Loki. Enable the exporters in your static config and point them to your monitoring stack.
metrics:
prometheus:
entryPoint: metrics
buckets:
- 0.1
- 0.3
- 1.2
- 5.0
log:
level: INFO
filePath: "/var/log/traefik.log"
format: json
Expose the metrics entrypoint (e.g., port 9100) and scrape it with Prometheus. The default dashboard includes request rates, latency histograms, and TLS handshake metrics, giving you a real‑time view of traffic health.
Pro tip: Setlog.format=jsonand ship logs to Loki or Elastic. Structured logs make it trivial to filter byrouterNameorserviceNamewhen troubleshooting.
Health checks
Traefik can perform active health checks on backend services. Define them per service to automatically remove unhealthy instances from the load‑balancer pool.
http:
services:
whoami-service:
loadBalancer:
servers:
- url: "http://whoami:80"
healthCheck:
path: "/healthz"
interval: "10s"
timeout: "2s"
If a container fails to respond to /healthz, Traefik stops routing traffic to it until it recovers.
Advanced routing patterns
Beyond simple host‑based routing, Traefik supports weighted round‑robin, path‑based splits, and even canary deployments. These patterns let you roll out new versions with zero downtime.
Weighted traffic for canary releases
http:
services:
api-v1:
loadBalancer:
servers:
- url: "http://api-v1:80"
api-v2:
loadBalancer:
servers:
- url: "http://api-v2:80"
api-composite:
weighted:
services:
- name: api-v1
weight: 90
- name: api-v2
weight: 10
Attach api-composite to a router. 90 % of traffic goes to the stable version, while 10 % exercises the new release – perfect for testing in production.
Path prefix routing
http:
routers:
blog-router:
rule: "PathPrefix(`/blog`)"
service: blog-service
middlewares:
- strip-blog-prefix
middlewares:
strip-blog-prefix:
stripPrefix:
prefixes:
- "/blog"
Here the router forwards any request that starts with /blog to the blog-service, but the middleware removes the prefix so the backend receives clean URLs.
Host + Path combination
Complex SaaS platforms often need both host and path matching. Traefik’s rule syntax lets you chain conditions with &&:
rule: "Host(`app.example.com`) && Path(`/v1/*`)"
This router will only match version 1 API calls on the main application domain, keeping other versions isolated.
Real‑world use case: Multi‑tenant SaaS platform
Imagine a SaaS product that hosts separate subdomains for each customer (customer1.example.com, customer2.example.com) and provides both a web UI and an API. Requirements include: automatic TLS, per‑tenant rate limiting, and zero‑downtime deployments.
Using Traefik’s Docker provider, each customer’s stack can be launched with a single docker-compose.yml. Labels encode routing, TLS, and middleware rules, keeping the platform’s core configuration static.
# docker-compose.yml (customer stack)
version: "3.9"
services:
web:
image: myapp/web:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.web-${CUSTOMER}.rule=Host(`${CUSTOMER}.example.com`)"
- "traefik.http.routers.web-${CUSTOMER}.entrypoints=websecure"
- "traefik.http.routers.web-${CUSTOMER}.tls.certresolver=myresolver"
- "traefik.http.routers.web-${CUSTOMER}.middlewares=rate-${CUSTOMER}"
- "traefik.http.middlewares.rate-${CUSTOMER}.rateLimit.average=200"
- "traefik.http.middlewares.rate-${CUSTOMER}.rateLimit.burst=50"
api:
image: myapp/api:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.api-${CUSTOMER}.rule=Host(`${CUSTOMER}.example.com`) && PathPrefix(`/api`)"
- "traefik.http.routers.api-${CUSTOMER}.entrypoints=websecure"
- "traefik.http.routers.api-${CUSTOMER}.tls.certresolver=myresolver"
- "traefik.http.routers.api-${CUSTOMER}.middlewares=api-auth-${CUSTOMER}"
- "traefik.http.middlewares.api-auth-${CUSTOMER}.basicAuth.users=${API_USERS}"
When a new customer signs up, the platform runs a small script that injects the CUSTOMER environment variable and spins up the stack. Traefik instantly discovers the new containers, provisions TLS, and enforces the per‑tenant rate limit – all without a single reload.
Pro tip: Store the generated docker-compose.yml in a GitOps repository. A CI pipeline can validate the YAML, apply security scans, and then trigger the deployment via a webhook to your orchestrator.