Skip to content

DNS01 challenge

The DNS01 challenge proves to an ACME server (for example, Let's Encrypt) that you control a domain. You prove it with a DNS TXT record. cert-manager creates and removes this record through the API of your DNS provider.

DNS01 is the only challenge type that issues wildcard certificates. It also works for domains that are not reachable from the internet. It does not work for IP addresses.

Resource chain

cert-manager creates one resource per step. To debug an issue, follow this chain:

Certificate -> CertificateRequest -> Order -> Challenge (one per DNS name)
kubectl get certificate,certificaterequest,order,challenge -n <namespace>
kubectl describe challenge <name> -n <namespace>

How the process works

  1. cert-manager creates an Order on the ACME server for the DNS names of the Certificate.
  2. The ACME server returns one authorization per DNS name. Each authorization contains a token.
  3. cert-manager creates a Challenge resource for each DNS name.
  4. cert-manager selects a solver from the solvers list of the Issuer.
  5. The solver creates a TXT record at _acme-challenge.<domain>. The value is a key computed from the token.
  6. cert-manager runs a self check until it sees the record (see below).
  7. cert-manager tells the ACME server that the challenge is ready.
  8. The ACME server reads the TXT record from public DNS. If the value is correct, the ACME server accepts the authorization.
  9. cert-manager finalizes the Order and stores the certificate in the Secret.
  10. cert-manager deletes the TXT record.

While cert-manager waits, the Challenge shows State: pending and Reason: Waiting for dns-01 challenge propagation.

Self check

The self check makes sure that the TXT record exists before cert-manager asks the ACME server to read it. The purpose is to not send failed challenges to the ACME server.

By default, the self check does two steps:

  1. It uses the recursive nameservers from /etc/resolv.conf of the controller pod to find the authoritative nameservers (SOA lookup).
  2. It queries the authoritative nameservers directly for the TXT record.

A recursive nameserver resolves names for clients and caches the answers. An authoritative nameserver holds the real records of a zone.

Controller flags that change the process

Flag Default Effect
--dns01-recursive-nameservers empty Replaces /etc/resolv.conf with this list of nameservers. Format is host:port, comma separated. DNS-over-HTTPS URLs (https://...) are also accepted.
--dns01-recursive-nameservers-only false The self check only queries the configured recursive nameservers. It never queries the authoritative nameservers. The check can take longer because of the cache of the recursive nameservers.
--dns01-check-retry-period 10s Time between two self checks.
--issuer-ambient-credentials false Lets an Issuer use ambient credentials (for example IRSA or EKS Pod Identity). A ClusterIssuer uses them by default.

With Helm, you set these flags in extraArgs:

extraArgs:
  - --dns01-recursive-nameservers-only
  - --dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53

Use these flags in the following cases:

  • Split-horizon DNS: the cluster DNS returns a private zone and the public zone has the TXT record. The self check never passes.
  • The cluster DNS filters SOA queries (for example, dnsmasq with --filterwin2k). cert-manager cannot find the zone.
  • A firewall blocks port 53 from the cluster to the authoritative nameservers. Use --dns01-recursive-nameservers-only.

Solver selection

An Issuer can have more than one solver. cert-manager selects one solver per DNS name with the selector field:

Selector Match rule
dnsNames Exact DNS name. It has priority over dnsZones.
dnsZones The zone and all its subdomains. The longest zone wins (sys.example.com wins over example.com for www.sys.example.com).
matchLabels All labels must be on the Certificate.
no selector Matches all names. A more specific selector wins over it.

If two solvers match at the same level, the solver with more matchLabels wins. If they are still equal, the first solver in the list wins.

Delegation with CNAME

You can delegate the challenge to a different zone. Then cert-manager only needs write access to that zone. Create this record one time in the main zone:

_acme-challenge.example.com  CNAME  _acme-challenge.less-privileged.example.org

The cnameStrategy field of the dns01 solver controls this:

  • None (default): cert-manager writes the TXT record at _acme-challenge.<domain>. It does not follow a CNAME.
  • Follow: cert-manager follows the CNAME records and writes the TXT record in the target zone.
solvers:
  - selector:
      dnsZones:
        - example.com
    dns01:
      cnameStrategy: Follow
      route53:
        region: eu-central-1
        hostedZoneID: <zone-id-of-less-privileged.example.org>

If you use Follow, cert-manager follows any CNAME at _acme-challenge.<domain>. This includes a CNAME that a wildcard record creates. Make sure that no wildcard CNAME exists in the zone, or the TXT record goes to the wrong zone.

Route53 solver

Field Effect
region AWS region of the API calls.
hostedZoneID Hosted zone to change. If empty, cert-manager finds the zone with ListHostedZonesByName.
accessKeyID / accessKeyIDSecretRef and secretAccessKeySecretRef Static credentials. If empty, cert-manager uses ambient credentials.
role IAM role that cert-manager assumes before it changes the zone.
auth.kubernetes.serviceAccountRef Service account token used to assume role (web identity).

The IAM policy needs route53:GetChange, route53:ChangeResourceRecordSets, route53:ListResourceRecordSets and route53:ListHostedZonesByName. You can limit ChangeResourceRecordSets to TXT records with the route53:ChangeResourceRecordSetsRecordTypes condition.