Need email infrastructure? Try PostScale -- transactional email API built in the EU. PostScale

    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:

    1. Add — create the TXT record via the DNScale API
    2. Wait — give the record time to propagate across DNScale's authoritative nameservers
    3. 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:

    1. Sign in at app.dnscale.eu.
    2. Go to API Keys and create a new key.
    3. Grant the minimum scopes: zones:read, records:read, records:write.
    4. Scope the key to specific zones if you can — e.g. only example.com if 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-dnscale

    Create the credentials file:

    # /etc/letsencrypt/dnscale.ini
    dns_dnscale_api_token = <your-token>
    chmod 600 /etc/letsencrypt/dnscale.ini

    Issue 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 = 60

    Remove 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 \
      run

    Caddy (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: 60

    And 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.

    1. Install the webhook via Helm:

      helm install cert-manager-webhook-dnscale \
        oci://ghcr.io/dnscaleou/charts/cert-manager-webhook-dnscale \
        --namespace cert-manager
    2. Store the token as a Kubernetes secret:

      apiVersion: v1
      kind: Secret
      metadata:
        name: dnscale-api-token
        namespace: cert-manager
      stringData:
        api-token: <your-token>
    3. 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
    4. 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 +short

    Orphan _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