Traefik Proxy v3.x in a Docker Swarm stack with pre-provisioned Let’s Encrypt SSL/TLS Certificates
November 22, 2024•378 words
You can find all of code for this project in https://github.com/jstilwell/traefik-ssl-docker-swarm. Pull requests are welcomed if you think anything can be improved.
Use Case
There is a lot of documentation available for using Traefik Proxy's ACME provider to provision Let's Encrypt SSL certificates, but there isn't much information available for using Let's Encrypt SSL certificates after they have already been provisioned.
For my use case, it was a Docker Swarm running on a RHEL host with only LAN egress, and the certificates were provisioned and managed by another department, but they are automatically renewed and obey the standard directory structure of Let's Encrypt certificates. They live in /etc/letsencrypt/archive/fqdn.example.com and are sym-linked to /etc/letsencrypt/live/fqdn.example.com.
Notes & Requirements
You will need an environment with a functioning Docker Swarm installation, as well as SSL certificates that have already been generated via certbot or something similar.
You also need to change the volume mappings to match your environment, and any usage of your-fqdn.example.com needs to reflect your own FQDN (fully qualified domain name). Otherwise, this should be pretty plug & play.
The Code
./config/certificates.toml
:
[[tls.certificates]]
certFile = "/etc/traefik/certs/fullchain.pem"
keyFile = "/etc/traefik/certs/privkey.pem"
docker-compose.yaml
services:
traefik:
# Application proxy via labels, middlewares, and rules.
# Docs: https://doc.traefik.io/traefik/
image: "traefik:v3.1.4"
networks:
- ingress-proxy
command:
- "--log.level=INFO"
# Entrypoints
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.http.tls=true"
# Redirect HTTP to HTTPS
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--entrypoints.web.http.redirections.entryPoint.permanent=true"
# Swarm Provider - https://doc.traefik.io/traefik/providers/swarm/
- "--providers.swarm=true"
- "--providers.swarm.endpoint=unix:///var/run/docker.sock"
- "--providers.swarm.exposedbydefault=false"
# Dynamic Configuration - https://doc.traefik.io/traefik/providers/file/
- "--providers.file.directory=/etc/traefik/dynamic_conf"
# Logging
- "--accesslog=true"
- "--accesslog.filePath=/var/log/traefik/access.log"
# API and Dashboard
- "--api=true"
- "--api.dashboard=true"
- "--api.insecure=false"
ports:
- 80:80
- 443:443
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- ${PWD}/logs:/var/log/traefik
- ${PWD}/config:/etc/traefik/dynamic_conf
- /etc/letsencrypt/archive/your-fqdn.example.com/fullchain1.pem:/etc/traefik/certs/fullchain.pem:ro
- /etc/letsencrypt/archive/your-fqdn.example.com/privkey1.pem:/etc/traefik/certs/privkey.pem:ro
deploy:
mode: global
placement:
constraints:
- "node.role==manager"
labels:
- "traefik.enable=true"
- "traefik.http.routers.websecure.rule=(Host(`your-fqdn.example.com`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`)))"
- "traefik.docker.network=ingress-proxy"
- "traefik.http.routers.websecure-router.entrypoints=websecure"
- "traefik.http.routers.websecure-router.tls=true"
- "traefik.http.routers.websecure-router.service=api@internal"
- "traefik.http.services.websecure-service.loadbalancer.server.port=8080"
whoami:
image: "traefik/whoami"
command:
- --port=2001
- --name=iamfoo
deploy:
replicas: 1
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`your-fqdn.example.com`) && PathPrefix(`/whoami`)"
- "traefik.http.services.whoami.loadbalancer.server.port=2001"
- "traefik.docker.network=ingress-proxy"
networks:
ingress-proxy:
driver: overlay
# NETWORKNAME_ingress-proxy: # If the network already exists, comment above and use the external instead.
# external: true
Clone the Repo
git clone https://github.com/jstilwell/traefik-ssl-docker-swarm.git
Deploy the Stack
sudo docker stack deploy --with-registry-auth -c docker-compose.yaml INGRESS-PROXY