Zero‑Config TLS with Traefik: Let’s Encrypt ACME and Dynamic Service Discovery
Traefik’s ACME integration with dynamic providers eliminates manual certificate work. This post walks through challenge choices, a Docker Compose example, and the operational limits you’ll hit in production.
02 Sept 2026, 11:29 UTC

The problem: manual certs in container fleets
Running dozens of services behind a single edge router used to mean generating a certificate for every hostname, copying the PEM files into each container, and remembering to renew them before they expire. When services appear and disappear automatically — thanks to Docker Swarm, Kubernetes, or a Consul catalog — that manual workflow becomes a bottleneck and a source of downtime.
How Traefik automates ACME
Traefik’s built‑in ACME client (the lego library) watches the dynamic providers you configure (Docker labels, Kubernetes Ingress/CRD, Consul Catalog, etc.). When a router is created with tls: true and a certResolver reference, Traefik immediately requests a certificate from Let’s Encrypt, stores it in the backend you defined (file, Consul, etcd, Redis), and renews it 30 days before expiry. No per‑service manifests, no secret objects, no cron jobs.
Choosing the challenge type
- HTTP‑01 – requires port 80 reachable on the entryPoint that Traefik listens on. Simplest for public‑facing clusters.
- TLS‑ALPN‑01 – uses port 443; useful when port 80 is blocked but you can terminate TLS on Traefik itself.
- DNS‑01 – delegates proof to your DNS provider (Cloudflare, Route 53, etc.). Enables wildcard certificates and works without any inbound ports, but needs API credentials with zone‑write permission.
Each challenge has operational trade‑offs: HTTP‑01 fails if another process binds port 80; TLS‑ALPN‑01 conflicts with any other TLS terminator on 443; DNS‑01 adds a dependency on the DNS provider’s API and on lego’s supported providers list.
Worked example: Docker Compose with HTTP‑01
The following docker-compose.yml spins up Traefik v3.x and a sample whoami service. Traefik listens on ports 80/443, uses the myresolver ACME resolver (staging server for safe testing), and stores certificates in /letsencrypt/acme.json on a named volume.
version: "3.8"
services:
traefik:
image: traefik:v3.0
command:
- "--api.dashboard=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.email=[contact removed]"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "letsencrypt:/letsencrypt"
networks:
- proxy
whoami:
image: traefik/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls=true"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"
networks:
- proxy
volumes:
letsencrypt:
networks:
proxy:
external: false
Deploy with docker compose up -d. In Traefik’s logs you should see Obtaining certificate for whoami.example.com followed by Certificate obtained. Visiting https://whoami.example.com in a browser (after adding a hosts entry for the staging CA) shows the familiar “Hostname, IP, Headers” page served over a valid TLS connection.
Trade‑offs and limits
- Rate limits – Let’s Encrypt allows 50 certificates per registered domain per week and 5 duplicate certificates per week. A misconfigured loop can exhaust the quota quickly. Always start with the staging endpoint (
acme-staging-v02.api.letsencrypt.org) and monitor theX-RateLimit-Remainingheader in debug logs. - Storage backend – The file‑based
acme.jsonworks for a single Traefik instance. In a replicated setup you must switch to a shared backend (Consul, etcd, Redis) otherwise each replica will request its own certificate, doubling the rate‑limit consumption. - Wildcard certificates – Only DNS‑01 can issue
*.example.com. If your DNS provider isn’t inlego’s supported list you’ll need a custom webhook or manual DNS updates, breaking full automation. - Co‑existence with cert‑manager – In Kubernetes, running both Traefik’s ACME and cert‑manager on the same domain leads to ownership conflicts. Designate one controller per domain.
What to verify next
- Check the storage backend (
acme.jsonor Consul keys) for a certificate entry with anotAfterdate ~90 days out and a SAN list matchingwhoami.example.com. - Simulate renewal: advance the system clock 31 days or wait; Traefik logs should show
Renewing certificateand the new cert replaces the old without dropping connections. - Test challenge failure: temporarily block port 80 (for HTTP‑01) or revoke DNS API credentials (for DNS‑01) and confirm Traefik logs the error and retries per its back‑off schedule.
- Add a global HTTPS redirect middleware:
Then--entrypoints.web.http.redirections.entrypoint.to=websecure --entrypoints.web.http.redirections.entrypoint.scheme=https --entrypoints.web.http.redirections.entrypoint.permanent=truecurl -I http://whoami.example.comshould return301withLocation: https://whoami.example.com/and anStrict-Transport-Securityheader.
With these pieces in place you get a fully automated TLS edge that scales with your service catalogue — no manual cert handling, no secret distribution, and a single source of truth for certificate lifecycle.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.