Dynamic TLS Certificate Handling in Traefik: Auto-Detecting New Certs Without Restart

I’m setting up Traefik to dynamically handle user-provided domains and generate custom TLS certificates manually and placing it inside a specific directory, then allowing traefik to auto-resolve https traffic without modifying traefik_dynamic.yml or restarting Traefik.

in my setup:

  • users wil bind a custom domain to my IPv4 address (traefik container's 443)
  • certificates will be generated by me externally (via Certbot, not ACME)
  • certificates are stored in /etc/traefik/certs/{domain}/fullchain.pem and privkey.pem. i’m using providers.file.directory with watch: true to automatically detect changes.

my issue is, traefik still serves the default certificate (throws error for the new domain) even after adding a valid cert and key for a new domain in the above said directory (/etc/traefik/certs/{domain})

is traefik capable of watching certificate files dynamically and resolve tls traffic with the domain name set on the folder name inside /etc/traefik/certs (which is a custom directory where i keep all of user's certificates)? the problem is, i'm implementing this in a larger scale, and i can't be modifying traefik_dynamic.yml file each time to register a certificate directory.

is there any ways with which the domains can be dynamically registered, and traefik can auto-find the keys of the certificates with domain name sent in request, resolve https traffic without restarting traefik and dynamically modifying yml files?

my docker-compose.yaml:

services:
  traefik:
    image: traefik:v2.9
    container_name: traefik
    command:
      - "--providers.docker=true"
      - "--providers.file.directory=/etc/traefik/"
      - "--providers.file.watch=true"   # Enable automatic certificate detection
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--log.level=DEBUG"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "./traefik.yml:/etc/traefik/traefik.yml"
      - "./traefik_dynamic.yml:/etc/traefik/traefik_dynamic.yml"
      - "./certs:/etc/traefik/certs"  # Mount certificate directory
    restart: always

my traefik.yml file:

global:
  checkNewVersion: false
  sendAnonymousUsage: false

log:
  level: DEBUG  # Options: ERROR, WARN, INFO, DEBUG

api:
  dashboard: true  # Enable Traefik Dashboard
  insecure: true   # ⚠️ Only for testing. Disable in production.

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

providers:
  docker:
    exposedByDefault: false  # Avoid exposing all containers automatically
  file:
    directory: "/etc/traefik"  # Watching for certificate changes
    watch: true               # Enables automatic reload of certificates

my traefik_dynamic.yml file:

http:
  routers:
    nondocker:
      entryPoints:
        - web
        - websecure
      rule: "HostRegexp(`{subdomain:[a-z0-9-.]+}.domain.com`)"
      service: someservice
      middlewares:
        - add-route-path  # Attach middleware here
      tls: {}

  middlewares:
    add-route-path:
      replacePathRegex:
        regex: "^/(.*)"  # Match everything after /
        replacement: "/route.php"  # Rewrite request path to /route.php
  
  services:
    someservice:
      loadBalancer:
        servers:
          - url: "http://somewhere:80"


tls:
  certificates:
    - certFile: "/etc/traefik/certs/domain.com/fullchain.pem"
      keyFile: "/etc/traefik/certs/domain.com/privkey.pem"
  stores:
    default:
      defaultCertificate:
        certFile: "/etc/traefik/certs/domain.com/fullchain.pem"
        keyFile: "/etc/traefik/certs/domain.com/privkey.pem"
  options:
    default:
      minVersion: VersionTLS12

When you add a cert to a TLS file, you need to "touch" the watched dynamic config file for Traefik to reload, without restart.

hi! thanks for your reply.

but i've tried that, and also restarted traefik container as well, and i got this error in docker logs:

time="2025-02-17T19:58:10Z" level=debug msg="Serving default certificate for request: \"sample.umarfarooq.site\""
time="2025-02-17T19:58:10Z" level=debug msg="Serving default certificate for request: \"sample.umarfarooq.site\""
time="2025-02-17T19:58:10Z" level=debug msg="http: TLS handshake error from 103.98.63.142:35255: remote error: tls: unknown certificate"
time="2025-02-17T19:58:10Z" level=debug msg="http: TLS handshake error from 103.98.63.142:35126: remote error: tls: unknown certificate"

the certificate of the domain sample.umarfarooq.site is in /etc/traefik/certs/sample.umarfarooq.site/ but it didn't recognize the certificate dynamically with its domain name. i've also kept watch: true in traefik.yml for the directory /etc/traefik to listen for new certificates, all in vain.

what am i doing wrong in this setup?

I can add TLS certs to config, touch traefik-dynamic.yml, then Traefik reloads the config and certs.

1 Like

yes that worked, i pulled v2.9 instead of latest, so that caused the problem. but, im facing a new issue. as you said, modiying dynamic file and touching it worked, but when i listen /etc/traefik/dynamic folder by adding watch: true to file provider, but if i add a config (yml file) inside /etc/traefik/dynamic folder, and touching it, it reloads the config (i can see it in docker logs) but the configuration was not reloaded. even restarted the traefik container. didnt work. but it worked for traefik_dynamic.yml

can we make traefik to listen to multiple config files inside /etc/traefik/dynamic and execute them?

my traefik.yml (file provider only)

providers:
  file:
    directory: "/etc/traefik/dynamic"  # Watching for certificate changes
    watch: true               # Enables automatic reload of certificates

dynamic1.yml inside /etc/traefik/dynamic:

tls:
  certificates:
    - certFile: "/etc/traefik/certs/domain.com/fullchain.pem"
      keyFile: "/etc/traefik/certs/domain.com/privkey.pem"

I quickly testet traefik:v3.3 on a Linux server with Docker. With

      - --providers.file.directory=/traefik-dynamic
      - --providers.file.watch=true

Traefik reloads config when I create a new dynamic config file and when I update the dynamic config file.

This indicates something is wrong with your setup.