Traefik Reverse Proxy for Docker Services: Practical Production Setup
When you run multiple Docker services on one host, port management quickly becomes messy. Traefik solves this by acting as a dynamic reverse proxy that discovers containers through labels and routes requests automatically.
This guide shows a production-friendly baseline with automatic TLS, clean routing, and a secure dashboard strategy.
Architecture Overview
A common setup includes:
- One external entrypoint on ports 80/443
- One internal Docker network shared by Traefik and apps
- App-specific labels that define hostnames and middleware
- ACME certificate resolver for automatic HTTPS
Traefik watches Docker events and updates routes without manual reloads.
1) Create the shared network
docker network create edge
Use this network for Traefik and all exposed application containers.
2) Deploy Traefik with Docker Compose
Example docker-compose.yml for Traefik:
services:
traefik:
image: traefik:v3.1
container_name: traefik
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.letsencrypt.acme.email=admin@example.com
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge=true
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
- --api.dashboard=true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
networks:
- edge
networks:
edge:
external: true
Create the ACME file with strict permissions:
mkdir -p letsencrypt
touch letsencrypt/acme.json
chmod 600 letsencrypt/acme.json
3) Route an app with labels
Example app service:
services:
app:
image: ghcr.io/example/myapp:latest
restart: unless-stopped
networks:
- edge
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=letsencrypt
- traefik.http.services.myapp.loadbalancer.server.port=8080
DNS for app.example.com must point to your server IP.
4) Secure operational endpoints
Avoid exposing dashboard endpoints unauthenticated. If you need dashboard access, protect it with middleware:
labels:
- traefik.http.middlewares.admin-auth.basicauth.users=admin:$$apr1$$...
- traefik.http.routers.traefik.rule=Host(`traefik.example.com`)
- traefik.http.routers.traefik.service=api@internal
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.tls.certresolver=letsencrypt
- traefik.http.routers.traefik.middlewares=admin-auth
5) Production Hardening Tips
- Set
exposedByDefault=false(already enabled above) - Keep Docker socket read-only
- Use firewall rules to allow only required ports
- Apply rate limiting middleware for public endpoints
- Add header middleware (HSTS, frame protections, etc.)
Example secure headers middleware:
- traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000
- traefik.http.middlewares.secure-headers.headers.browserXssFilter=true
- traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true
Then attach middleware to routers.
Verification Checklist
docker ps
docker logs traefik --tail=100
curl -I https://app.example.com
Expected results:
- Valid certificate issued
HTTP/2 200(or app expected status)- No certificate errors in browser
Summary
Traefik is a strong choice for Docker-first environments where services change frequently. With labels-driven routing and automatic TLS, you get a clean operational model and less manual reverse proxy maintenance.
The biggest reliability gains come from network segmentation, secure dashboard access, and explicit middleware policies per app.