Traefik: Reverse Proxy for Docker with Automatic SSL
Traefik is a modern reverse proxy designed for Docker. Unlike Nginx, it automatically detects launched containers and exposes them without modifying the configuration: just add labels to the container.
02
File structure
traefik/
├── docker-compose.yml
├── traefik.yml # static configuration
└── data/
├── acme.json # Let's Encrypt certificates (create empty)
└── traefik.log
bash
mkdir -p traefik/data
touch traefik/data/acme.json
chmod 600 traefik/data/acme.json # REQUIRED
03
Static configuration (traefik.yml)
yaml
# traefik/traefik.yml
api:
dashboard: true
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: admin@yourdomain.com
storage: /data/acme.json
httpChallenge:
entryPoint: web
providers:
docker:
exposedByDefault: false # containers must explicitly opt-in
file:
filename: /traefik.yml
log:
filePath: /data/traefik.log
level: INFO
04
docker-compose.yml for Traefik
yaml
# traefik/docker-compose.yml
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./data:/data
networks:
- traefik_net
labels:
- "traefik.enable=true"
# Dashboard (protect with authentication in production)
- "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
networks:
traefik_net:
external: true
bash
# Create the shared network
docker network create traefik_net
# Start Traefik
docker compose up -d
05
Expose an application with Traefik
Add labels to your app container: Traefik reads them automatically:
yaml
# app/docker-compose.yml
services:
myapp:
image: nginx:alpine
networks:
- traefik_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`app.yourdomain.com`)"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
- "traefik.http.services.myapp.loadbalancer.server.port=80"
networks:
traefik_net:
external: true
SSL is automatically issued from Let's Encrypt on first access.
06
Practical examples
WordPress + MySQL
yaml
services:
wordpress:
image: wordpress:latest
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wp
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: password
networks:
- traefik_net
- internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.wp.rule=Host(`blog.yourdomain.com`)"
- "traefik.http.routers.wp.tls.certresolver=letsencrypt"
- "traefik.http.services.wp.loadbalancer.server.port=80"
db:
image: mariadb:11
environment:
MARIADB_DATABASE: wp
MARIADB_USER: wpuser
MARIADB_PASSWORD: password
MARIADB_ROOT_PASSWORD: rootpassword
volumes:
- db_data:/var/lib/mysql
networks:
- internal
networks:
traefik_net:
external: true
internal:
volumes:
db_data:
Protection with HTTP basic authentication
bash
# Generate password (htpasswd)
apt install apache2-utils -y
htpasswd -nb admin YourPassword
# Output: admin:$apr1$...
yaml
labels:
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$..."
- "traefik.http.routers.myapp.middlewares=auth"
In docker-compose.yml files, $ in htpasswd passwords must be doubled ($$) to prevent Docker from interpreting them as variables.
07
Certificate renewal
Traefik automatically renews Let's Encrypt certificates before expiration. No manual action needed.
To force renewal:
bash
docker stop traefik
rm traefik/data/acme.json
touch traefik/data/acme.json && chmod 600 traefik/data/acme.json
docker start traefik
Related articles
