Overview
Traefik v3 is a modern reverse proxy and load balancer that integrates smoothly with Docker, automatically discovering containers and routing traffic to them. In this guide, you will deploy Traefik v3 as a Docker reverse proxy with automatic HTTPS using Let’s Encrypt via the DNS challenge (Cloudflare as an example). The setup is fast, reproducible, and ideal for hosting multiple apps on a single server with clean domain names and valid TLS certificates.
Prerequisites
Before you start, you will need: a Linux server (Ubuntu 22.04/24.04 works great) with root or sudo access, Docker and Docker Compose installed, a domain name you control (e.g., example.com), ports 80 and 443 open to the server, and an API token from your DNS provider (Cloudflare in this tutorial) with permission to manage DNS (Zone:DNS:Edit). Create A or AAAA records for the subdomains you plan to use (e.g., traefik.example.com, app.example.com) pointing to your server’s public IP.
Step 1: Create a dedicated Docker network
Why: Keeping a shared “proxy” network lets Traefik see and route to other containers without exposing them on the host. Run:
docker network create proxy
Step 2: Prepare your working directory
Create a folder for Traefik and a place to store Let’s Encrypt certificates. The ACME store must be persistent across restarts.
mkdir -p ~/traefik/letsencrypt
cd ~/traefik
Step 3: Create a .env file for secrets
To avoid hardcoding tokens in your Compose file, use a .env file in the same directory. Replace values with your own.
CF_DNS_API_TOKEN=your_cloudflare_dns_token_here
[email protected]
TRAEFIK_DOMAIN=traefik.example.com
APP_DOMAIN=app.example.com
Step 4: Write docker-compose.yml
The Compose file below launches Traefik v3 and a sample “whoami” app to test routing and TLS. It enables the Docker provider, sets HTTP to HTTPS redirection, configures Let’s Encrypt with the Cloudflare DNS challenge, and exposes the dashboard on a secure subdomain.
version: "3.9"
services:
traefik:
image: traefik:v3.1
container_name: traefik
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
- --entryPoints.web.http.redirections.entryPoint.to=websecure
- --entryPoints.web.http.redirections.entryPoint.scheme=https
- --certificatesResolvers.le.acme.dnsChallenge.provider=cloudflare
- --certificatesResolvers.le.acme.email=${LETSENCRYPT_EMAIL}
- --certificatesResolvers.le.acme.storage=/letsencrypt/acme.json
- --log.level=INFO
- --api.dashboard=true
environment:
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
networks:
- proxy
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`${TRAEFIK_DOMAIN}`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=le"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.middlewares.secure.headers.stsSeconds=31536000"
- "traefik.http.middlewares.secure.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.secure.headers.stsPreload=true"
- "traefik.http.middlewares.secure.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.secure.headers.browserXssFilter=true"
whoami:
image: traefik/whoami:latest
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.who.rule=Host(`${APP_DOMAIN}`)"
- "traefik.http.routers.who.entrypoints=websecure"
- "traefik.http.routers.who.tls.certresolver=le"
- "traefik.http.routers.who.middlewares=secure@docker"
- "traefik.http.services.who.loadbalancer.server.port=80"
restart: unless-stopped
networks:
proxy:
external: true
Step 5: Start Traefik and verify certificates
Bring the stack up and watch the logs for the ACME flow. Traefik will create a DNS TXT record via the API token and obtain certificates automatically.
docker compose up -d
docker logs -f traefik
On first successful issuance, an acme.json file will be created under the letsencrypt folder. Set strict permissions on it to keep private keys safe.
chmod 600 ./letsencrypt/acme.json
Step 6: Test the routes
Open https://traefik.example.com to see the dashboard and https://app.example.com to reach the whoami test application. Both should show a valid TLS lock in your browser. If you use Cloudflare proxy (orange cloud), DNS challenge still works because the validation is done via DNS records, not HTTP.
Optional: Protect the dashboard
Do not leave the dashboard open. You can add basic auth using a middleware. Create a bcrypt or Apache MD5 hash and replace the placeholder below. For a quick hash, you can use the “htpasswd” utility.
labels:
- "traefik.http.middlewares.dash-auth.basicauth.users=admin:$$apr1$$replace$$hashvaluehere"
- "traefik.http.routers.dashboard.middlewares=dash-auth@docker,secure@docker"
Adding your own apps
To publish another container behind Traefik, attach it to the “proxy” network and add three labels: a Host rule for your domain, the HTTPS entrypoint, and the certresolver. Optionally apply the “secure” headers middleware. Example:
labels:
- "traefik.enable=true"
- "traefik.http.routers.blog.rule=Host(`blog.example.com`)"
- "traefik.http.routers.blog.entrypoints=websecure"
- "traefik.http.routers.blog.tls.certresolver=le"
- "traefik.http.routers.blog.middlewares=secure@docker"
Troubleshooting
Port 80/443 already in use: Stop any existing web servers (e.g., Nginx, Apache) or change their ports. Traefik must bind to 80 and 443 to handle redirection and TLS termination.
Certificate issuance fails: Check Traefik logs for ACME errors. Verify the Cloudflare token has Zone:DNS:Edit on the correct zone, and that your environment variable is loaded by Compose. If you are using multiple zones, the token must cover them too.
404 or 502 errors: Ensure your container has traefik.enable=true, the Host rule matches your requested domain, DNS records resolve to your server, and the service port label points to the right container port.
Slow or failed DNS propagation: Give it a minute after creating DNS records. If using split DNS on a LAN, make sure internal resolvers return the public IP.
Why DNS challenge?
The DNS challenge avoids opening custom ports for validation and supports wildcard certificates. It is reliable behind CDNs, in NAT environments, and when you want to cover many subdomains without adding each one by hand.
What you achieved
You now have a production-ready Traefik v3 reverse proxy on Docker with automatic HTTPS, security headers, and a pattern you can reuse for any containerized app. Add services, set Host rules, and Traefik will handle routing and certificate management for you. This setup cuts down on boilerplate, centralizes TLS, and keeps your stack maintainable as it grows.
Comments
Post a Comment