Installing and Configuring Traefik as Your Reverse Proxy
We earn commissions when you shop through the links on this page, at no additional cost to you. Learn more.
Traefik transformed how I manage multiple services on my homelab. Unlike Nginx, which requires manual configuration for each service, Traefik automatically discovers Docker containers and routes traffic to them—no restart needed. I'm going to walk you through installing Traefik, configuring it with Let's Encrypt SSL, and getting it running with real Docker services in under an hour.
Why Traefik Over Nginx or Caddy?
I've used all three, and here's my honest take: Traefik shines when you run many services in Docker and want zero downtime updates. When I deploy a new container with the right labels, Traefik picks it up instantly. Nginx requires manual configuration edits and reloads. Caddy is simpler but less flexible for complex routing rules.
The trade-off is complexity. Traefik's configuration file is longer, and its concepts (entrypoints, routers, services, middlewares) take time to grok. But once you understand the mental model, you'll never go back.
If you're running this on a budget VPS—say a $40/year option from RackNerd with 2GB RAM—Traefik uses roughly 80–120 MB, leaving plenty for your actual services. That's the sweet spot I've found for small self-hosted setups.
Prerequisites and What You Need
You'll need:
- A Linux VPS or homelab machine with Docker and Docker Compose installed
- A domain name pointing to your server's IP (e.g.,
example.com) - Port 80 and 443 open to the internet
- About 100 MB of disk space for Traefik itself
- An email address for Let's Encrypt certificate renewal notifications
I'm running this on a Debian 12 machine, but the Docker Compose approach works on Ubuntu, CentOS, or any modern Linux distro.
Setting Up the Directory Structure
First, create a dedicated folder for Traefik and its config:
mkdir -p /opt/traefik/config
cd /opt/traefik
mkdir -p data
touch data/traefik.yml
touch data/acme.json
chmod 600 data/acme.json
The acme.json file stores your SSL certificates. It must have restrictive permissions (600) or Let's Encrypt will refuse to write to it.
Creating the Traefik Configuration File
I prefer defining Traefik's core settings in a YAML file rather than command-line flags. Create data/traefik.yml:
api:
insecure: false
dashboard: true
debug: false
entryPoints:
web:
address: ":80"
http:
redirections:
entrypoint:
regex: "^http://(.*)$"
replacement: "https://$1"
permanent: true
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik-network
file:
filename: /etc/traefik/config.yml
watch: true
certificatesResolvers:
letsencrypt:
acme:
email: "[email protected]"
storage: /etc/traefik/acme.json
httpChallenge:
entryPoint: web
log:
level: INFO
filePath: /var/log/traefik.log
accessLog:
filePath: /var/log/traefik-access.log
Replace [email protected] with a real email address. This is where Let's Encrypt sends renewal reminders. The exposedByDefault: false setting means only containers I explicitly label will be exposed—better security.
The Docker Compose Setup
Now create docker-compose.yml in your /opt/traefik directory:
version: '3.8'
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- traefik-network
ports:
- "80:80"
- "443:443"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/etc/traefik/traefik.yml:ro
- ./data/acme.json:/etc/traefik/acme.json
- ./data/config.yml:/etc/traefik/config.yml:ro
- /var/log/traefik:/var/log/traefik
environment:
- TZ=UTC
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(\`traefik.example.com\`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
- "traefik.http.routers.traefik.middlewares=auth@file"
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stopped
networks:
- traefik-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(\`whoami.example.com\`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
traefik-network:
driver: bridge
volumes:
acme-data:
traefik.example.com and whoami.example.com with your actual domain names. Traefik uses these hostnames to route traffic, so DNS must resolve them to your server's IP.I've included a test service, whoami, which echoes back your request headers. It's perfect for verifying Traefik is routing correctly.
Creating a Middleware Config File
For the dashboard to be secure, create data/config.yml with basic auth:
http:
middlewares:
auth:
basicAuth:
users:
- "admin:$apr1$sR8n/w/8$bCMZZn3y7mM7sL5s0f9Zz0"
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
The hash above is for username admin and password password. To generate your own, use:
htpasswd -nb admin yourpassword | openssl enc -base64
Launching Traefik
From your /opt/traefik directory, start it up:
docker-compose up -d
docker-compose logs -f traefik
Watch the logs for a few seconds. You should see Traefik detecting the whoami container and requesting SSL certificates from Let's Encrypt. This typically takes 30–60 seconds.
Connecting Existing Services
To add an existing service to Traefik, add these labels to your Docker container:
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(\`myapp.example.com\`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
- "traefik.http.services.myapp.loadbalancer.server.port=8080"
Replace myapp with your service name and 8080 with the port it listens on inside the container. Traefik will auto-detect it and route traffic.
myapp.example.com where the router is myapp. This makes debugging much easier when you have 10+ services.Monitoring and Troubleshooting
Check container status:
docker-compose ps
View real-time logs:
docker-compose logs -f traefik
Common issues I've hit:
- Certificate not issuing: Check that port 80 is accessible and DNS resolves correctly. Let's Encrypt needs to reach your server on HTTP.
- 503 Service Unavailable: The backend service isn't running or Traefik can't reach the port you specified. Verify the port in the Docker container matches your label.
- Slow dashboard: Disable
insecure: trueon the API (already done in my config) and always access it over HTTPS with auth.
Performance and Resource Usage
On a 2GB VPS, Traefik with 15+ services uses roughly 100–150 MB RAM and nearly zero CPU at idle. Under moderate load (a few requests per second), CPU bumps to 5–10%. It's very efficient compared to running separate Nginx instances for each service.
If you're on a truly budget setup—say a $40/year RackNerd entry-level VPS—this will work fine. You'll have room for one or two additional Docker services without breaking a sweat.
Next Steps
Once you're comfortable with basic routing, explore Traefik's middleware options: rate limiting, request rewriting, compression, and custom headers. I use middleware to add security headers to all my services in one place, which beats configuring each app individually.
Also consider running watchtower alongside Traefik to auto-update your containers. With Traefik's dynamic discovery, you can redeploy services without touching the reverse proxy config—pure infrastructure bliss.
Discussion