Skip to main content

Certificates

Traefik obtains all certificates from Let’s Encrypt, using two different challenge methods for two different kinds of hostname.

Why two

HTTP-01 requires Let’s Encrypt’s validation servers to reach the host on port 80. The admin surfaces have no public A record — that is the point of them — so HTTP-01 can never validate them. DNS-01 validates by publishing a TXT record instead, which works for any hostname regardless of whether it is publicly reachable. It needs an API-driven way to write that record, which is what dns-manager provides.

The DNS-01 path

services/dns-manager/app.py exposes three endpoints: Two implementation details matter enough that they are called out in the source doc as the mistakes that were actually made:
  1. The TXT value is not the token. ACME DNS-01 requires base64url(SHA256(keyAuth)). Publishing the token field verbatim fails validation.
  2. dns-manager must return immediately. Traefik handles the propagation wait itself via delayBeforeCheck: 30s. If the webhook sleeps, Traefik times out instead.
Traefik also does not interpolate ${VAR} in its YAML config, so the ACME email is hardcoded in traefik.yml while the CA server is passed through Docker Compose, which does interpolate.

Rate limits

Let’s Encrypt production allows 50 certificates per registered domain per week and 5 validation failures per hostname per hour. Because of that, make deploy-infra uses staging certificates by default; production certificates require make deploy-infra-production or the deploy-infra.yml workflow, which sets them explicitly. The edge stack is deliberately excluded from push-triggered deploys for the same reason — recreating Traefik re-requests certificates, and that should be a decision rather than a side effect of a merge.

Source documents