22.6. Traefik
Table of Contents
Why Traefik?
Traefik is a modern HTTP reverse proxy and load balancer, designed specifically with containers and dynamic infrastructure in mind. While Nginx and similar servers work very well, they were originally built for static configuration files. Traefik focuses on:
- Automatic service discovery (for example, from Docker, Kubernetes, Consul)
- Dynamic configuration that updates when containers start or stop
- Built‑in support for HTTPS, including automatic Let's Encrypt certificates
- Simple routing rules that integrate nicely with labels or annotations
For a backend developer who uses Docker or container orchestration, Traefik often requires much less manual configuration than traditional proxies.
Traefik’s Core Concepts
To understand Traefik, you need a set of basic concepts. They are similar to what you saw in the Reverse Proxies and Nginx chapters, but Traefik has its own terminology.
EntryPoints
An EntryPoint defines where Traefik listens for incoming traffic. For example:
- HTTP on port 80
- HTTPS on port 443
- Custom internal admin port
A minimal Traefik configuration might define two EntryPoints: web and websecure.
Example static configuration (YAML style):
entryPoints:
web:
address: ":80"
websecure:
address: ":443"You can think of an EntryPoint as: “Which port does Traefik open and what protocol does it use?”
Routers
A Router connects an EntryPoint to a specific backend service, based on rules that match each incoming request.
Routers can match on:
- Host name, for example
Host("api.example.com") - Path prefix, for example
PathPrefix("/api") - Methods, headers, and more
Router responsibilities:
- Decide if a request applies to them (using rules)
- Attach middlewares (for example authentication, redirects)
- Choose which service to route to
- Optionally enforce HTTPS, TLS, etc.
Example:
http:
routers:
api-router:
rule: "Host(`api.example.com`) && PathPrefix(`/`)"
entryPoints:
- websecure
service: api-service
tls: {}
Here, any HTTPS request on websecure whose Host is api.example.com is sent to api-service.
Services
A Service in Traefik represents one or more backend servers, for example your FastAPI app running in a Docker container or multiple replicas behind load balancing.
A simple service might be defined as:
http:
services:
api-service:
loadBalancer:
servers:
- url: "http://app1:8000"
- url: "http://app2:8000"
Traefik will load balance requests between app1 and app2.
Middlewares
Middlewares are filters or modifiers applied between the client and the service. Typical uses:
- Redirect HTTP to HTTPS
- Add or remove headers
- Strip or add a path prefix
- Rate limiting
- Basic auth
Example middleware that redirects HTTP to HTTPS:
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
permanent: trueThen you attach it to a router:
http:
routers:
http-router:
rule: "Host(`api.example.com`)"
entryPoints:
- web
middlewares:
- redirect-to-https
service: api-serviceProviders
Providers are where Traefik discovers configuration from. Common providers include:
dockerfor Docker labelskubernetesfor Kubernetes Ingress or CRDsfilefor static or dynamic YAML/TOML filesconsulCatalog,etcd, etc.
When you use Docker, Traefik reads labels on your containers and automatically creates routers, services, and middlewares.
Static vs Dynamic Configuration
Traefik splits configuration into:
- Static configuration: defined at startup. It includes:
- EntryPoints
- Providers
- API/dashboard configuration
- Logging options
- Dynamic configuration: can change while Traefik is running. It includes:
- Routers
- Services
- Middlewares
- TLS options and certificates
Static configuration is often set through a traefik.yml or command line flags, and dynamic configuration is often provided by Docker labels or additional config files that Traefik watches.
Important rule: Static configuration requires a restart of Traefik to change, dynamic configuration does not. Use dynamic configuration for things that depend on running applications, like routes and backends.
Running Traefik with Docker
For backend projects, Traefik shines when combined with Docker and Docker Compose. Instead of editing configuration files for every new service, you add a set of labels to containers.
Basic `docker-compose.yml` with Traefik
Here is a minimal example that exposes a FastAPI container behind Traefik, without HTTPS yet:
version: "3.8"
services:
traefik:
image: traefik:v3.0
command:
- "--api.dashboard=true"
- "--api.insecure=true"
- "--entrypoints.web.address=:80"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
ports:
- "80:80"
- "8080:8080" # Dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
api:
image: my-fastapi-image
container_name: api
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.localhost`)"
- "traefik.http.routers.api.entrypoints=web"
- "traefik.http.services.api.loadbalancer.server.port=8000"Explanation of the important parts:
--providers.docker=true: tells Traefik to read Docker labels.--providers.docker.exposedbydefault=false: containers are not exposed unlesstraefik.enable=trueis set.- The
apiservice: traefik.http.routers.api.rule: the routing rule. It matchesHost("api.localhost").traefik.http.routers.api.entrypoints=web: routes from thewebEntryPoint.traefik.http.services.api.loadbalancer.server.port=8000: tells Traefik the internal port of the container.
With this setup:
- When you open
http://api.localhostin your browser (and map the host name to127.0.0.1in/etc/hostson Linux/macOS or Hosts file on Windows), Traefik forwards the request to theapicontainer. - You can open
http://localhost:8080to see the Traefik dashboard.
Mapping Hostnames for Local Development
In local development, you typically do not own a domain. You can still use host-based routing by editing your hosts file.
For example, add:
127.0.0.1 api.localhost
127.0.0.1 web.localhostThen you can create different routers for different services:
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.localhost`)"
- "traefik.http.routers.web.rule=Host(`web.localhost`)"This lets you simulate multiple subdomains on your laptop.
Using Traefik for HTTPS and Let's Encrypt
One of Traefik’s most popular features is automatic certificate management with Let's Encrypt.
Basic HTTPS Configuration with ACME
Here is an example static configuration snippet for Traefik using Let's Encrypt:
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: "you@example.com"
storage: "/letsencrypt/acme.json"
httpChallenge:
entryPoint: webThis instructs Traefik to:
- Listen on
web(80) andwebsecure(443). - Use Let's Encrypt to obtain certificates.
- Use the HTTP challenge on port 80.
- Store certificates in
/letsencrypt/acme.json.
In Docker Compose:
services:
traefik:
image: traefik:v3.0
command:
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
Now your services can reference the letsencrypt resolver to get HTTPS certificates.
Creating an HTTPS Router for Your API
Your Dockerized FastAPI service might look like this:
services:
api:
image: my-fastapi-image
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=8000"Traefik will:
- Terminate TLS on
websecurewith a certificate obtained forapi.example.com. - Forward decrypted HTTP traffic to the
apicontainer on port 8000.
Redirecting HTTP to HTTPS
You usually want all HTTP traffic on port 80 redirected to HTTPS.
You can apply a middleware through labels:
services:
traefik:
# same as above
api:
image: my-fastapi-image
labels:
- "traefik.enable=true"
# HTTPS router
- "traefik.http.routers.api-https.rule=Host(`api.example.com`)"
- "traefik.http.routers.api-https.entrypoints=websecure"
- "traefik.http.routers.api-https.tls.certresolver=letsencrypt"
- "traefik.http.routers.api-https.service=api"
# HTTP -> HTTPS router
- "traefik.http.routers.api-http.rule=Host(`api.example.com`)"
- "traefik.http.routers.api-http.entrypoints=web"
- "traefik.http.routers.api-http.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.services.api.loadbalancer.server.port=8000"api-httplistens onweb(port 80).- It applies the
redirect-to-httpsmiddleware. - The middleware sends a redirect to the HTTPS version of the URL.
Common Middleware Examples
Middlewares in Traefik can solve many everyday problems in backend APIs.
1. Stripping a Path Prefix
Imagine your application expects / but you want to expose it at /api. Instead of changing your app, you can use stripPrefix.
Example:
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.example.com`) && PathPrefix(`/api`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.routers.api.middlewares=api-strip"
- "traefik.http.middlewares.api-strip.stripprefix.prefixes=/api"
- "traefik.http.services.api.loadbalancer.server.port=8000"Client requests:
https://api.example.com/api/users
Traefik forwards:
http://api:8000/users
2. Adding Security Headers
You can add common security headers without changing your application code.
labels:
- "traefik.http.middlewares.sec-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.sec-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.sec-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.sec-headers.headers.browserXssFilter=true"
- "traefik.http.routers.api.middlewares=sec-headers"
These labels configure a sec-headers middleware which is attached to the api router.
Important security tip: Do not rely only on Traefik for security. Add secure headers, rate limiting, and authentication at the proxy level, but also validate inputs and protect your application itself.
3. Basic Authentication for Internal Tools
You might want a simple password prompt for an internal dashboard.
labels:
- "traefik.http.middlewares.simple-auth.basicauth.users=user:$$apr1$$0AD..../$F5X..."
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.middlewares=simple-auth"
- "traefik.http.routers.dashboard.service=api@internal"
Here, api@internal is Traefik’s own dashboard service. The password is a hashed value generated by tools like htpasswd.
Load Balancing with Traefik
Traefik supports multiple load balancing algorithms, such as:
wrr(weighted round robin)drr(dynamic round robin) in newer versions
Typical use case, multiple replicas of an API:
services:
api1:
image: my-fastapi-image
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=8000"
api2:
image: my-fastapi-image
labels:
- "traefik.enable=true"
# Share the same service name "api"
- "traefik.http.services.api.loadbalancer.server.port=8000"
Traefik groups containers with the same service configuration and balances traffic between them.
You can also set weights:
labels:
- "traefik.http.services.api.loadbalancer.server.port=8000"
- "traefik.http.services.api.loadbalancer.sticky.cookie=true"
- "traefik.http.services.api.loadbalancer.serversTransport=mytransport"Or in file-based configuration, you can specify weights per server.
Monitoring and Debugging Traefik
Traefik provides:
- A dashboard
- Access logs
- Metrics for Prometheus and others
Enabling the Dashboard
You can enable the (insecure) dashboard in development via command options:
command:
- "--api.dashboard=true"
- "--api.insecure=true"
Then open http://localhost:8080 to see:
- EntryPoints
- Routers
- Services
- Middlewares
This is extremely useful when learning or debugging, but you must avoid --api.insecure=true in production.
Access Logs
Access logs help you see what requests Traefik handles:
command:
- "--accesslog=true"
- "--accesslog.format=json"You can pipe these logs to a central logging system or inspect them directly.
When to Choose Traefik vs Nginx
Both Traefik and Nginx can act as reverse proxies for your backend.
A short comparison:
| Feature | Traefik | Nginx |
|---|---|---|
| Dynamic discovery | Native (Docker, Kubernetes, etc.) | Manual or using extra tooling |
| Configuration style | Labels, TOML/YAML | Static config files |
| Automatic HTTPS (Let's Encrypt) | Built-in ACME support | Via additional tools or custom config |
| Dashboard | Built-in, very visual | Third-party or manual |
| Best suited for | Container-based, dynamic environments | Static or traditional server setups |
As an absolute beginner:
- If you use Docker for almost everything, Traefik can simplify your setup.
- If you deploy on a single VPS with only a few fixed services, Nginx is also a great choice.
You do not have to commit to one forever. You can start with whichever seems easier, then learn the other later.
Example: FastAPI Behind Traefik in Production
To tie everything together, here is a simplified production-style Compose configuration for a FastAPI backend with PostgreSQL, using Traefik for HTTPS.
version: "3.8"
services:
traefik:
image: traefik:v3.0
command:
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--accesslog=true"
- "--api.dashboard=true"
ports:
- "80:80"
- "443:443"
- "8080:8080" # remove or protect in real production
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
api:
image: my-fastapi-image
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/app
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.routers.api.middlewares=sec-headers@docker"
- "traefik.http.services.api.loadbalancer.server.port=8000"
- "traefik.http.middlewares.sec-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.sec-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.sec-headers.headers.stsIncludeSubdomains=true"
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=app
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:In this example:
- Traefik terminates HTTPS and routes
https://api.example.comtraffic to your FastAPI app. - Traefik automatically obtains and renews Let's Encrypt certificates.
- Security headers are added by a middleware.
- PostgreSQL is not exposed directly to the internet, only to the internal Docker network.
This pattern is common in real-world backend systems. Traefik becomes the single entry point for all HTTP(S) traffic, and your backend services remain simple, focused, and unaware of TLS, host-based routing, or redirects.
By understanding these Traefik basics, you can design and deploy container-based backend systems that are easier to manage, more secure, and highly flexible as you add more services over time.
Views: 8
KAHIBARO