Traefik in Practice: Automatically Provision HTTPS Certificates and Proxy Docker Services
Traefik is a reverse proxy software that has stronger automation capabilities compared to traditional tools. For example, it can automatically detect changes in backend services, update routing configurations in real-time without restarting; it also has built-in support for ACME, which can automatically request and renew HTTPS certificates.
This article focuses on the integration of Traefik and Docker and implements automatic HTTPS configuration, other functions are not involved.
The reference is the official documentation: https://doc.traefik.io/traefik/v3.4/user-guides/docker-compose/acme-dns/
Different from the official documentation, we place Traefik and backend services in different compose files and connect them to a shared network through an external network, which decouples the services more. It also better demonstrates Traefik’s ability to automatically detect and create routes. For example, if the Traefik service starts first, it begins listening to Docker container events, if it detects a backend service configured with Traefik labels, it can automatically create routes; conversely, if the backend service starts first and Traefik starts later, Traefik will scan the running containers once, find the matching containers, and automatically create routes.
Create a Shared Network
As mentioned above, we use a shared network to use Traefik. Run the following command to create it:
docker network create traefik-proxy
Configure and Run Traefik Service
Create a docker-compose.yaml
file:
services:
traefik:
image: "traefik:v3.4"
restart: "unless-stopped"
ports:
- "443:443"
command:
# Comment out these two lines, they are only used during development and debugging
# - "--log.level=DEBUG"
# - "--api.insecure=true"
# Indicates Traefik will listen to Docker container events and
# automatically discover services
- "--providers.docker=true"
# By default, containers are not exposed,
# only containers explicitly labeled with "traefik.enable=true" will be proxied.
- "--providers.docker.exposedbydefault=false"
# Listen on port 443 to receive HTTPS requests
- "--entryPoints.websecure.address=:443"
# Enable the resolver named dnsresolver_cf,
# which automatically requests HTTPS certificates using DNS challenge.
- "--certificatesresolvers.dnsresolver_cf.acme.dnschallenge=true"
# Specify which provider dnsresolver_cf uses, here we use Cloudflare,
# so Traefik will use Cloudflare's API to complete DNS verification and
# obtain Let's Encrypt certificates. More providers can be found at:
# https://doc.traefik.io/traefik/v3.4/https/acme/#providers
- "--certificatesresolvers.dnsresolver_cf.acme.dnschallenge.provider=cloudflare"
# Specify the storage location for dnsresolver_cf's certificates
- "--certificatesresolvers.dnsresolver_cf.acme.storage=/letsencrypt/cf.json"
environment:
# Environment variables required by the Cloudflare provider,
# this token is obtained from the Cloudflare dashboard,
# and needs permissions for Zone:Zone:Read, Zone:DNS:Edit.
- "CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}"
volumes:
# Certificate storage location
- "./letsencrypt:/letsencrypt"
# Must mount this, this is the Unix Socket interface of the Docker daemon,
# Traefik uses it to listen to container events.
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- "traefik-proxy"
networks:
traefik-proxy:
external: "true"
name: "traefik-proxy"
Create a .env
file. Because we use the CF_DNS_API_TOKEN
environment variable, we need to create this file in the same folder as docker-compose.yaml
:
CF_DNS_API_TOKEN=your_token
Finally, run the Traefik service with the following command:
docker compose up -d
Configure and Run whoami
whoami is the backend service mentioned above, it is a simple HTTP server.
Create a docker-compose.yaml
file:
services:
whoami:
image: "traefik/whoami"
restart: "unless-stopped"
labels:
# Because we configured "--providers.docker.exposedbydefault=false",
# this service needs to be explicitly exposed.
- "traefik.enable=true"
# Define the router rule: when the Host in the request is
# whoami.mwum.com (change to your own domain),
# trigger this router. The router name is whoami, which can be changed,
# but note that the entrypoints / tls configuration below must match.
- "traefik.http.routers.whoami.rule=Host(`whoami.mwum.com`)"
# Route the websecure entrypoint configured by
# "--entryPoints.websecure.address=:443" to the whoami service
- "traefik.http.routers.whoami.entrypoints=websecure"
# The certresolver here corresponds to the one enabled by
# "--certificatesresolvers.dnsresolver_cf.acme.dnschallenge=true"
- "traefik.http.routers.whoami.tls.certresolver=dnsresolver_cf"
networks:
- "traefik-proxy"
networks:
traefik-proxy:
external: "true"
name: "traefik-proxy"
Run the whoami service with the following command:
docker compose up -d
Further Explanation of Traefik’s Automatic Discovery
Now we can access the whoami service via HTTPS at https://whoami.mwum.com/.
Notice that we didn’t configure anything related to whoami in the Traefik service definition, this would be unimaginable with a traditional reverse proxy. In Docker, we use labels to declaratively tell Traefik whether to proxy a service, which host to match, how to route traffic, and which certificate resolver to use. And Traefik is able to automatically discover the whoami service.
You might also wonder: since we didn’t expose any ports for whoami, how does Traefik know where to forward the traffic?
We can run the following command on the whoami service to see:
$ docker compose ps
Name Command State Ports
------------------------------------------
whoami_whoami_1 /whoami Up 80/tcp
whoami does not expose any ports externally, only exposes port 80 inside the container. We also did not configure Traefik to listen to any port of the whoami service, but Traefik by default automatically reads the EXPOSE
port of the whoami service as the backend address for the target service.
Next: Future chapters, waiting for you to traverse and meet again