Traefik: first steps
If Ansible helped me declare the desired state of my machines, I still needed a simple, automatic way to route HTTP/HTTPS traffic to my various services (Docker, VMs, bare metal). After several recommendations, I dove into Traefik, a reverse proxy / ingress controller written in Go.
Why I chose Traefik
- Dynamic configuration: Traefik continuously queries its providers (Docker, Kubernetes, files…) and updates routes on the fly, no more
nginx -s reloadevery time a container shows up. - Built-in Let’s Encrypt: certificate generation and renewal without a homemade script.
- Clear dashboard: you can see routes, middlewares, and entry points in real time.
- Native Docker/K8s support: three labels are enough to expose a service.
My first discoveries
- Quick install
docker network create traefik
docker compose -f traefik.yml up -d
Static vs dynamic Static: ports, providers, ACME. Dynamic: routing rules, middlewares, certificates, changeable without a restart.
Docker labels Minimal example:
labels: - "traefik.enable=true" - "traefik.http.routers.blog.rule=Host(`blog.example.com`)" - "traefik.http.services.blog.loadbalancer.server.port=3000"Let’s Encrypt in 30 seconds A
certresolver=lein the labels plus an email address, and Traefik handles the rest.Middlewares HTTPS redirect, security headers, basic auth… declarable via YAML or a label.
Full example: Traefik + Vaultwarden over HTTPS
1. Traefik stack (traefik.yml)
services:
traefik:
image: traefik:v3.0
restart: unless-stopped
command:
- '--providers.docker=true'
- '--providers.docker.exposedByDefault=false'
- '--entrypoints.web.address=:80'
- '--entrypoints.websecure.address=:443'
- '[email protected]'
- '--certificatesresolvers.le.acme.storage=/certs/acme.json'
- '--certificatesresolvers.le.acme.tlschallenge=true'
- '--api.dashboard=true'
ports:
- '80:80'
- '443:443'
volumes:
- '/var/run/docker.sock:/var/run/docker.sock:ro'
- './certs:/certs'
networks:
- traefik
networks:
traefik:
external: true
2. Exposed service: Vaultwarden (vaultwarden.yml)
version: '3.9'
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
WEBSOCKET_ENABLED: "true"
DOMAIN: "https://vault.example.com"
SMTP_HOST: "smtp.example.net"
SMTP_FROM: "[email protected]"
SMTP_PORT: "587"
SMTP_SECURITY: "starttls"
SMTP_USERNAME: "smtp-user"
SMTP_PASSWORD: "smtp-pass"
volumes:
- ./bw-data:/data
networks:
- traefik
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.vault.rule=Host(`vault.example.com`)'
- 'traefik.http.routers.vault.entrypoints=websecure'
- 'traefik.http.routers.vault.tls.certresolver=le'
- 'traefik.http.services.vault.loadbalancer.server.port=80'
- 'traefik.http.routers.vault.middlewares=vault-redirect'
- 'traefik.http.middlewares.vault-redirect.redirectscheme.scheme=https'
networks:
traefik:
external: true
What I like most
- Zero downtime: adding or changing a service doesn’t interrupt any connection.
- Real-time dashboard: debugging is super easy, you immediately see the active rules.
- Self-managed certificates: no more Let’s Encrypt renewal cron jobs.
What I liked (a little) less
| Friction | Detail |
|---|---|
| Verbose labels | A complex service → 10-15 labels; it’s hard on the eyes. |
| K8s-style hierarchy | Entrypoint → Router → Middleware → Service: takes some getting used to. |
| Multi-provider priorities | Docker + K8s + File: watch out for rule conflicts. |
Things to explore
- Traefik plugins: custom middlewares (rate-limit, OAuth…)
- ForwardAuth + Keycloak: SSO for self-hosted apps
- Prometheus monitoring: detailed metrics
- Redundancy: testing in swarm or K8s cluster
Conclusion
Traefik replaced my homemade Nginx configs. Today I have a reverse proxy that updates itself, SSL certificates that renew without me thinking about it, and a dashboard where I can see everything at a glance.
Next step: centralized authentication and community plugins.