Traefik Proxy v3.x in a Docker Swarm stack with pre-provisioned Let’s Encrypt SSL/TLS Certificates

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

You'll only receive email when they publish something new.

More from Jesse Stilwell
All posts