Docker + Traefik: A Clean Self-Hosting Setup
Running a handful of services on a VPS used to mean managing Nginx configs, remembering to renew certs, and manually proxying ports. Traefik makes all of that go away — and the integration with Docker is genuinely good.
The idea: Traefik sits in front of everything, watches the Docker socket for containers coming and going, and routes traffic based on labels you put on each container. TLS is handled automatically via Let’s Encrypt. You add a new service, label it, and it’s live with HTTPS in under a minute.
The Traefik container
A minimal docker-compose.yml for Traefik:
services:
traefik:
image: traefik:v3.0
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.le.acme.tlschallenge=true
- --certificatesresolvers.le.acme.email=you@example.com
- --certificatesresolvers.le.acme.storage=/certs/acme.json
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./certs:/certs
restart: unless-stopped
exposedbydefault=false is important — without it, every container on the host gets a route, which is not what you want.
Adding a service
Any service you want to expose gets a few labels:
services:
myapp:
image: myapp:latest
labels:
- traefik.enable=true
- traefik.http.routers.myapp.rule=Host(`app.example.com`)
- traefik.http.routers.myapp.entrypoints=websecure
- traefik.http.routers.myapp.tls.certresolver=le
- traefik.http.services.myapp.loadbalancer.server.port=8080
restart: unless-stopped
Traefik picks up the labels on container start, requests a cert if it doesn’t have one, and starts routing. The tls.certresolver=le line is the only TLS config you need per service.
HTTP → HTTPS redirect
A global redirect middleware handles the 80 → 443 redirect without adding it to every service:
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
What I don’t use
Traefik has a dashboard, metrics, and a full middleware ecosystem. For a personal setup, most of it is noise. I keep it as close to the minimum config as possible — the less Traefik is doing, the less there is to debug when something breaks.
The Docker provider is also not the right choice for anything that needs fine-grained control over routing logic or has strict security requirements around socket exposure. For that, the file provider with explicit config is worth the extra work. For personal projects and side services, the Docker provider is hard to beat.