Let's Encrypt DNS-01 Challenges with DNScale
Automate Let's Encrypt certificate issuance and renewal with DNS-01 challenges using DNScale. Works for wildcard certificates, edge-proxied origins, and servers with no public port 80.
What you'll learn
- Understand when DNS-01 is the right ACME challenge type and when HTTP-01 is simpler
- Set up Let's Encrypt with DNScale using certbot, lego, acme.sh, or cert-manager
- Scope API tokens correctly so ACME clients have the minimum access they need
- Avoid common pitfalls ā propagation timeouts, CNAME delegation, CAA conflicts
Let's Encrypt supports three ACME challenge types: HTTP-01, TLS-ALPN-01, and DNS-01. For most public websites, HTTP-01 is the default because it's simple ā a file on port 80 proves you control the domain. But when HTTP-01 doesn't fit, DNS-01 is the escape hatch: instead of proving control via HTTP, you prove it by placing a TXT record in DNS.
DNScale's API lets any ACME client automate DNS-01 challenges against your zones. This guide covers when to use DNS-01, and how to set it up with the four most common Let's Encrypt clients.
When to use DNS-01
HTTP-01 breaks in four common situations:
- Wildcard certificates (
*.example.com). Let's Encrypt does not support HTTP-01 for wildcards ā DNS-01 is the only option. - Servers behind edge proxies or CDNs. If your origin sits behind a proxy (DNScale's Postscale edge network, Cloudflare, an ADC, etc.) the origin can't receive HTTP-01 validation traffic. DNS-01 bypasses the data plane entirely.
- No port 80 exposed. Firewalled origins, internal services, mail servers, and hosts behind strict NATs often have no public HTTP listener. DNS-01 needs no HTTP endpoint.
- Private or internal hostnames that still resolve publicly (split-horizon DNS, lab environments). HTTP-01 can't reach them; DNS-01 only needs DNS reachability for the ACME validator.
If none of those apply, HTTP-01 is usually simpler ā fewer moving parts, no API token to manage. DNS-01 is worth the extra setup only when you need it.
Tip: DNS-01 plays nicely with CAA records. Set
0 issue "letsencrypt.org"on your zone, and Let's Encrypt will be the only CA that can issue for it regardless of challenge type.
How DNS-01 works
For each domain on a certificate request, Let's Encrypt asks the client to publish a TXT record at _acme-challenge.<domain> containing a specific value. The ACME validator then queries public DNS for that record. If the value matches, the challenge passes.
Your ACME client drives three steps on each issuance or renewal:
- Add ā create the TXT record via the DNScale API
- Wait ā give the record time to propagate across DNScale's authoritative nameservers
- Clean up ā delete the TXT record after validation completes
All four clients covered below automate this flow end-to-end.
API token setup
Before any client can use DNS-01, create a DNScale API token:
- Sign in at app.dnscale.eu.
- Go to API Keys and create a new key.
- Grant the minimum scopes:
zones:read,records:read,records:write. - Scope the key to specific zones if you can ā e.g. only
example.comif that's the only domain the client will renew.
Store the token as you would any other secret: a file with mode 600, a secret manager, or an environment variable in your CI/CD pipeline. Never check it into a public repository.
Tip: Separate tokens per client. If certbot and cert-manager both issue certs on different hosts, use two tokens so you can rotate one without disrupting the other.
Client: certbot
The certbot-dns-dnscale plugin is published on PyPI.
pip install certbot-dns-dnscaleCreate the credentials file:
# /etc/letsencrypt/dnscale.ini
dns_dnscale_api_token = <your-token>chmod 600 /etc/letsencrypt/dnscale.iniIssue a certificate:
certbot certonly \
--authenticator dns-dnscale \
--dns-dnscale-credentials /etc/letsencrypt/dnscale.ini \
--dns-dnscale-propagation-seconds 60 \
-d example.com \
-d "*.example.com"Subsequent renewals (certbot renew) automatically use the same authenticator ā certbot stores the choice in /etc/letsencrypt/renewal/<name>.conf.
To convert an existing certificate from HTTP-01 to DNS-01, edit its renewal config:
[renewalparams]
authenticator = dns-dnscale
dns_dnscale_credentials = /etc/letsencrypt/dnscale.ini
dns_dnscale_propagation_seconds = 60Remove any webroot_path, standalone, or [[webroot_map]] leftover from the old authenticator.
Client: lego (including Caddy and Traefik)
Lego is the ACME library used by Caddy, Traefik, and the lego CLI. DNScale is available as a built-in DNS provider.
lego CLI:
DNSCALE_API_TOKEN=<your-token> lego \
--dns dnscale \
--domains example.com \
--domains "*.example.com" \
--email you@example.com \
runCaddy (Caddyfile):
example.com, *.example.com {
tls {
dns dnscale {env.DNSCALE_API_TOKEN}
}
reverse_proxy localhost:8080
}Set DNSCALE_API_TOKEN in Caddy's environment (systemd unit or container env).
Traefik (static config):
certificatesResolvers:
letsencrypt:
acme:
email: you@example.com
storage: /data/acme.json
dnsChallenge:
provider: dnscale
delayBeforeCheck: 60And set DNSCALE_API_TOKEN in Traefik's environment.
Client: acme.sh
The DNScale API plugin ships with acme.sh.
export DNSCALE_Token=<your-token>
acme.sh --issue --dns dns_dnscale -d example.com -d "*.example.com"acme.sh saves the token to ~/.acme.sh/account.conf on first use, so subsequent renewals don't need the env var. To rotate, edit that file or pass --renew-with-dnsapi dns_dnscale on the next renewal.
Client: cert-manager (Kubernetes)
For Kubernetes workloads, DNScale's cert-manager webhook plugs into cert-manager's external webhook interface.
-
Install the webhook via Helm:
helm install cert-manager-webhook-dnscale \ oci://ghcr.io/dnscaleou/charts/cert-manager-webhook-dnscale \ --namespace cert-manager -
Store the token as a Kubernetes secret:
apiVersion: v1 kind: Secret metadata: name: dnscale-api-token namespace: cert-manager stringData: api-token: <your-token> -
Define a ClusterIssuer referencing the webhook:
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-dnscale spec: acme: email: you@example.com server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-dnscale-account-key solvers: - dns01: webhook: groupName: acme.dnscale.eu solverName: dnscale config: apiTokenSecretRef: name: dnscale-api-token key: api-token -
Request a certificate as usual ā cert-manager will drive DNS-01 through the webhook.
Common pitfalls
Propagation too short
ACME validators cache DNS. If your client's "wait for propagation" is shorter than the TTL of negative caches and nearby resolvers, validation will fail. Start at 60 seconds. If you see intermittent failures, raise to 120.
CNAME delegation
If _acme-challenge.example.com is a CNAME to another name (a common pattern for delegating DNS-01 to a separate zone), the ACME validator follows the CNAME ā but your ACME client must publish the TXT at the target of the CNAME, not at _acme-challenge.example.com itself. All four clients above handle this automatically if configured with the correct zone.
CAA blocking Let's Encrypt
If your zone has a 0 issue "other-ca.com" CAA record and no entry for letsencrypt.org, Let's Encrypt will refuse to issue. Before moving a domain to DNS-01, confirm CAA allows the CA you're using:
dig CAA example.com +shortOrphan _acme-challenge.* TXT records
If cleanup fails silently (API error, plugin bug, client crash) the challenge TXT record may linger on your zone. These don't affect future issuance (every renewal uses a fresh random value), but they can clutter the zone over time. Periodically delete any _acme-challenge.* TXT records older than a few hours.
Rate limits
Let's Encrypt limits 50 certificates per registered domain per week and 5 duplicate certs per week. DNS-01 doesn't change these limits ā but if your client is stuck in a retry loop (e.g. a misconfigured propagation wait causing repeated failures) you can burn through the duplicate cert limit quickly. Always run --dry-run first when changing client or zone setup.
Which client should you pick?
- Standalone host with a single cert ā certbot. Familiar, well-documented, ships in every distro.
- Caddy or Traefik already in your stack ā use the built-in lego integration. No extra daemon.
- Minimal, shell-only host ā acme.sh. No Python, no compiled binary, just bash.
- Kubernetes ā cert-manager with the DNScale webhook.
Any of them works end-to-end with DNScale. Pick by where the rest of your infrastructure already sits.
Ready to manage your DNS with confidence?
DNScale provides anycast DNS hosting with a global network, real-time analytics, and an easy-to-use API.
Start free