Nginx is great until you have to write your 47th server block. Traefik flips the script: you add a few Docker labels, and it discovers, routes, and secures your services automatically. This Traefik reverse proxy guide shows you how to go from zero to production-grade HTTPS routing in under 30 minutes—no manual certificate management, no config file archaeology.
Want deeper dives? Check our related guides: Traefik Reverse Proxy Guide: Route HTTPS Traffic to Docker Services Automatically and The Complete Traefik Reverse Proxy Guide: HTTPS, Docker, and Let's Encrypt in Production. For a real-world deployment example, see Deploy Actual Budget with Docker Compose and Traefik.
What You'll Build
By the end of this guide, you'll have:
- Traefik running as a Docker container with automatic service discovery
- Let's Encrypt SSL certificates provisioned and renewed automatically
- HTTP-to-HTTPS redirection enforced globally
- Multiple backend services routed by hostname and path
- Basic middleware (compression, rate limiting, security headers) applied
- A working setup you can drop new services into without touching Traefik's config
Prerequisites
Before we start, make sure you have:
- Docker 24.0+ and Docker Compose v2 installed
- A Linux server (Ubuntu 22.04/24.04 LTS recommended) with ports 80 and 443 open
- A domain name with DNS A records pointing to your server (e.g.,
*.yourdomain.comor individual subdomains) - At least 1GB RAM and 1 CPU core (Traefik is lightweight; your apps are the hungry ones)
curlandjqinstalled for testing endpoints
Firewall note: Traefik needs ports 80 (HTTP challenge + redirect) and 443 (HTTPS) accessible. If you're using UFW:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
Step 1: Understand Traefik's Architecture
Traefik has three moving parts. You need to know them before the configs make sense:
- EntryPoints: The ports Traefik listens on. Typically
web(port 80) andwebsecure(port 443). - Routers: Match incoming requests (by Host, Path, headers) and decide which service handles them.
- Services: The actual backend—usually a Docker container, but also file, Kubernetes, or external URL.
- Middlewares: Modify requests/responses—compression, auth, rate limits, redirects, headers.
The magic: Traefik watches Docker events. When you spin up a container with labels, Traefik creates routers and services dynamically. No reloads. No restarts. It just works.
Step 2: Create the Traefik Base Configuration
Traefik uses two config layers: static (startup config, rarely changes) and dynamic (routing rules, changes constantly). We'll use a file for static config and Docker labels for dynamic.
2.1 Static Configuration File
Create traefik.yml in your project directory:
global:
checkNewVersion: false
sendAnonymousUsage: false
api:
dashboard: true
insecure: false # We'll secure this later
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
providers:
docker:
exposedByDefault: false
network: proxy
certificatesResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /letsencrypt/acme.json
tlsChallenge: {}
What's happening:
entryPoints.webhandles HTTP and redirects everything to HTTPSentryPoints.websecurehandles HTTPS with auto-managed certificatesproviders.docker.exposedByDefault: falsemeans containers need explicittraefik.enable=truelabelstlsChallengeuses the TLS-ALPN-01 challenge (works on port 443, no need for HTTP-01)
2.2 Docker Compose for Traefik
Create docker-compose.yml:
version: "3.8"
networks:
proxy:
external: false
services:
traefik:
image: traefik:v3.1
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./letsencrypt:/letsencrypt
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
Create the Let's Encrypt storage file with strict permissions:
mkdir -p letsencrypt
touch letsencrypt/acme.json
chmod 600 letsencrypt/acme.json
Launch Traefik:
docker compose up -d
Verify it's running:
curl -s http://localhost:80 | head -n 5
curl -s -o /dev/null -w "%{http_code}" https://traefik.yourdomain.com --insecure
You should get a 301 redirect from HTTP and a 200 (or 401) from HTTPS. The dashboard at https://traefik.yourdomain.com should show a valid Let's Encrypt certificate.
Step 3: Route Your First Service
Here's where Traefik shines. Add a new service to the same docker-compose.yml—no changes to Traefik needed.
3.1 Add a Whoami Test Service
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stopped
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.yourdomain.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
Deploy and test:
docker compose up -d whoami
curl -s https://whoami.yourdomain.com
You should see JSON with your request headers, hostname, and IP. Traefik automatically created the router, service, and requested an SSL certificate.
3.2 Path-Based Routing
Host-based routing is clean, but sometimes you need paths. Add an API service:
api:
image: traefik/whoami
container_name: api
restart: unless-stopped
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`app.yourdomain.com`) && PathPrefix(`/api`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.services.api.loadbalancer.server.port=80"
# Strip the /api prefix before forwarding
- "traefik.http.middlewares.api-stripprefix.stripprefix.prefixes=/api"
- "traefik.http.routers.api.middlewares=api-stripprefix"
Now https://app.yourdomain.com/api routes to the API container, with the /api prefix stripped before it hits the backend.
Step 4: Apply Middleware for Production Hardness
Routing is step one. Step two is not getting pwned. Traefik middlewares handle this elegantly.
4.1 Security Headers
Add a reusable security headers middleware to traefik.yml:
http:
middlewares:
security-headers:
headers:
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
customFrameOptionsValue: "SAMEORIGIN"
Apply it to any service:
- "traefik.http.routers.whoami.middlewares=security-headers@file"
4.2 Rate Limiting
Protect against brute force and scraping:
http:
middlewares:
rate-limit:
rateLimit:
average: 100
burst: 50
4.3 Compression
Enable gzip for text responses:
http:
middlewares:
compress:
compress: {}
Chain multiple middlewares on a router:
- "traefik.http.routers.whoami.middlewares=security-headers@file,rate-limit@file,compress@file"
Step 5: Secure the Dashboard
The Traefik dashboard is powerful—and dangerous if exposed. Lock it down with basic auth.
5.1 Generate a Password Hash
# Install htpasswd if you don't have it
sudo apt-get install -y apache2-utils
# Generate bcrypt hash (Traefik prefers bcrypt)
htpasswd -nbB admin "your_secure_password" | cut -d ":" -f 2
5.2 Add Basic Auth Middleware
Add to traefik.yml:
http:
middlewares:
dashboard-auth:
basicAuth:
users:
- "admin:$2y$10$...your_bcrypt_hash_here..."
Update the Traefik router labels in docker-compose.yml:
- "traefik.http.routers.traefik.middlewares=dashboard-auth@file,security-headers@file"
Restart Traefik:
docker compose restart traefik
Now https://traefik.yourdomain.com prompts for credentials. The dashboard shows your routers, services, middlewares, and certificate status in real time.
Step 6: Tips, Troubleshooting, and Production Gotchas
6.1 Certificate Not Issuing
If Let's Encrypt fails, check these in order:
- Port 443 must be open to the internet. TLS-ALPN-01 challenge happens on 443.
- DNS must resolve to your server before Traefik starts. Let's Encrypt validates the domain.
acme.jsonpermissions must be 600. Traefik refuses to write otherwise.- Rate limits: Let's Encrypt allows 50 certificates per domain per week. Don't hammer it.
Debug with Traefik's logs:
docker compose logs -f traefik | grep -i "certificate\|acme\|error"
6.2 Service Not Appearing in Dashboard
Common causes:
- Container is not on the
proxynetwork. All services and Traefik must share the same Docker network. - Missing
traefik.enable=truelabel. WithexposedByDefault: false, this is mandatory. - Router rule typo.
Host(`whoami.yourdomain.com`)needs backticks, not quotes.
6.3 502 Bad Gateway
Traefik can't reach the backend. Check:
- Is the backend container healthy?
docker compose ps - Is the port correct?
loadbalancer.server.portmust match what the container exposes. - Is the backend listening on
0.0.0.0or just127.0.0.1? Docker containers need to bind to all interfaces.
6.4 Performance Tuning
For high-traffic setups:
- Enable
--serversTransport.insecureSkipVerify=falseonly if you trust your internal network - Use
--providers.docker.watchfor faster container event polling - Consider a
redisorconsulprovider for multi-node Traefik clusters - Enable access logs and ship them to your SIEM for security monitoring
6.5 Backup Your ACME State
Your certificates live in letsencrypt/acme.json. Back it up:
cp letsencrypt/acme.json letsencrypt/acme.json.backup.$(date +%F)
Losing this file means re-requesting all certificates. Let's Encrypt rate limits make this painful.
Wrapping Up
You now have a fully automated reverse proxy that discovers services, routes traffic by hostname and path, terminates SSL with free Let's Encrypt certificates, and applies security middleware—all without writing a single Nginx config file.
Traefik's label-based configuration means adding a new service is literally just adding labels to your Docker Compose. No restarts. No reloads. No certificate management. It scales from a single VPS to a multi-node Kubernetes cluster with the same mental model.
That said, production edge cases—multi-node clustering, custom certificate authorities, TCP routing, canary deployments, and observability integration—deserve careful planning.
Need help architecting a production Traefik setup, migrating from Nginx, or integrating advanced middleware for your specific stack? Talk to our team—we've deployed Traefik at scale for SaaS platforms, fintech infrastructure, and healthcare compliance environments. We'll get your routing right.