Designing a Minimal TLS Termination Setup with Traefik and Let's Encrypt
A concise architecture note for a minimal Traefik TLS termination design using Let's Encrypt, covering requirements, trust boundaries, operational checks, failure modes, and when to redesign.
16 Jul 2025, 21:31 UTC

Requirements
To provide HTTPS for a service without manual certificate handling, the design must: automatically obtain and renew X.509 certificates from Let's Encrypt via the ACME protocol; expose a reachable endpoint for the HTTP‑01 challenge; store the private key and certificate material securely; and log renewal status for alerting.
Smallest Suitable Design
The minimal Traefik configuration consists of a single entrypoint for HTTP challenge, an ACME certificate resolver using the file store, and a router that forwards traffic to the backend service over TLS.
# traefik.yaml (static configuration)
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
certificatesResolvers:
le:
acme:
email: "[contact removed]"
storage: "acme.json"
httpChallenge:
entryPoint: web
# dynamic configuration (file provider)
http:
routers:
service-router:
rule: "Host(`service.example.com`)"
entryPoints:
- websecure
tls:
certResolver: le
services:
service-service:
loadBalancer:
servers:
- url: "http://127.0.0.1:8080"
This uses only the HTTP‑01 challenge (port 80) and stores the ACME account key and certificates in acme.json, which Traefik protects with file permissions.
Trust/Data Boundaries
The ACME resolver trusts the Let's Encrypt CA; the challenge validates that Traefik controls the domain by serving a token from /.well-known/acme-challenge/ on the web entrypoint. The private key for the account and the issued certificates reside in acme.json, which must be readable only by the Traefik process (e.g., 0600) to prevent tampering or key theft.
Operational Checks
- After startup, watch the logs for lines containing
acme: certificate issuedfor each domain. - Verify that
acme.jsonexists and contains aCertificateblock matching the domain. - Confirm that a TLS handshake on port 443 presents a certificate issued by Let's Encrypt (e.g., using
openssl s_client -connect service.example.com:443 -servername service.example.com). - Set up an alert on log messages matching
acme: erroror on the absence of a renewal log entry every 60 days.
Failure Modes
- DNS does not resolve to the Traefik host → ACME challenge fails → no certificate.
- Port 80 is blocked or used by another process → challenge cannot be served.
- Let's Encrypt rate limits (e.g., 50 certificates per domain per week) are exceeded → renewal blocked.
- File permissions on
acme.jsonare too permissive → private key exposure.
Conditions That Would Change the Design
If the environment cannot expose port 80 (e.g., only port 443 is allowed), switch to the TLS‑ALPN‑01 challenge, which requires the websecure entrypoint to handle the ACME protocol. If multiple Traefik instances need to share certificates, replace the file store with a distributed storage (e.g., Consul or KV) and ensure atomic updates to avoid duplicate issuance. Finally, if the organization uses an internal PKI, the ACME resolver would be removed and certificates would be supplied via the file provider or a secret manager.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.