DNS-01 Fails but the TXT Record Is There: Delegate _acme-challenge
You want a wildcard certificate, which means DNS-01 validation, which means your ACME client has to write a TXT record at _acme-challenge.example.com and Let's Encrypt has to read it back. But your DNS lives at a registrar with no API, or on a provider you would rather not hand automation credentials to, or your domain is split across multiple clouds. So the challenge fails, you read a forum thread, and the top answer says "increase the propagation wait time". You bump the sleep to five minutes. It still fails.
Longer sleeps do not fix this, because the problem is almost never propagation. The problem is where the record has to live and who is authoritative for it. The clean, permanent solution is to delegate just the _acme-challenge name, via a CNAME, to a small zone you fully control on a provider that does have an API. Your main DNS never needs automation, and the ACME client happily writes to the delegated zone instead.
create a one-time CNAME from _acme-challenge.example.com to a name in a zone you control with API access (for example example.com.acme.your-dns.net). Point your ACME client's DNS plugin at that second provider. Now every renewal writes the TXT record in the delegated zone, the CNAME leads the validator straight to it, and your primary DNS host never touches automation. Verify the chain with dig CNAME _acme-challenge.example.com and dig TXT against the authoritative server, not your local resolver.
The symptom: DNS-01 fails even though the TXT record "is there"
The client writes the record, waits, then Let's Encrypt reports it could not find the expected value:
Waiting for verification...
Challenge failed for domain example.com
DNS problem: NXDOMAIN looking up TXT for _acme-challenge.example.com
- check that a DNS record exists for this domain
Or the near-identical variant, no TXT record found for _acme-challenge.example.com. Meanwhile you can see the record in your provider's dashboard, and it may even resolve when you query it from your laptop. That contradiction, "it is there but the validator cannot see it", is the tell that this is a delegation or authority problem, not a timing one.
Why more wait time never fixes it
DNS-01 works like this: Let's Encrypt takes the base domain, prepends _acme-challenge, and does an authoritative lookup for a TXT record at that name. "Authoritative" is the key word. It does not ask your laptop's resolver or a cached copy; it follows the delegation chain from the root down to whichever nameservers are authoritative for that name, and reads the TXT there.
So the record must exist on the authoritative nameserver for _acme-challenge.example.com. If your ACME client wrote the TXT to one provider but the authoritative NS for that name is a different provider (because the zone is split, or an old delegation is still in place, or a subdomain was delegated elsewhere), the validator asks the authoritative server, gets nothing, and fails. No amount of sleeping changes which server is authoritative. You are waiting for a record to appear on a server that will never have it.
This is also why the record can resolve from your laptop but not for Let's Encrypt: your local resolver may be hitting a cache or a different path, while the validator always goes to the authority. Propagation delay is real and occasionally relevant, but it is the exception. Wrong authority is the rule.
Step 1: Create the delegated zone
On a DNS provider that has an API and an ACME plugin (many do; pick one you are comfortable giving a scoped token), create a zone you will use only for challenges, for example acme.your-dns.net. Inside it you will place TXT records named per domain, like example.com, giving the fully qualified example.com.acme.your-dns.net. Nothing else needs to live in this zone.
Step 2: Add the one-time CNAME on your primary DNS
On the DNS provider that is authoritative for example.com, add a single static CNAME. This is a manual, one-time change; it never needs updating again:
_acme-challenge.example.com. CNAME example.com.acme.your-dns.net.
For a wildcard *.example.com the challenge name is still _acme-challenge.example.com, so this one CNAME covers both the wildcard and the apex. If you also validate subdomains that are themselves delegated, each needs its own _acme-challenge.<sub> CNAME.
Step 3: Point the ACME client at the delegated provider
Configure your ACME client's DNS plugin with credentials for the delegated provider, not your primary one. With certbot and a DNS plugin, that is roughly:
certbot certonly \
--dns-your-provider \
--dns-your-provider-credentials /etc/letsencrypt/your-provider.ini \
-d example.com -d '*.example.com'
The client authenticates to the delegated provider, writes the TXT at example.com.acme.your-dns.net, and Let's Encrypt follows the CNAME from _acme-challenge.example.com to that record. Your primary DNS host never sees an API call. This is the same reason DNS-01 is the right validation method behind a proxy or across servers, which I covered in use DNS-01, not HTTP-01, behind a Cloudflare proxy.
Verification
Verify the chain against the authoritative server directly, the way Let's Encrypt does, not through your default resolver which may be caching. First confirm the CNAME exists and points where you expect:
dig +short CNAME _acme-challenge.example.com
# expected: example.com.acme.your-dns.net.
Then, during a renewal (or after manually placing a test TXT), confirm the TXT resolves at the end of the chain, querying the authoritative nameserver directly:
# find the authoritative NS for the delegated zone, then query it directly
dig NS acme.your-dns.net +short
dig @<that-nameserver> TXT example.com.acme.your-dns.net +short
If the CNAME resolves and the TXT is present on the authoritative server, Let's Encrypt will succeed. If dig against the authority returns nothing while your dashboard shows the record, you are looking at the wrong authority, which is the whole bug.
What people get wrong
"Increase the propagation sleep." The single most repeated non-fix. If the record is on the wrong authoritative server, it will never appear no matter how long you wait. Reach for dig @authoritative before you reach for a longer timeout.
"Query it from my laptop, it resolves, so DNS is fine." Your laptop's resolver is not the validator. It may serve a cached answer or take a different path. The only query that matters is one aimed at the authoritative nameserver for the challenge name.
"Give the ACME client full API access to my primary DNS." You can, but delegation exists precisely so you do not have to. Handing broad DNS credentials to an automated renewal on your production zone is a blast-radius you can avoid by scoping automation to a throwaway challenge zone.
"Use a CNAME to a name that does not exist yet." The CNAME target zone must actually exist and be authoritative before you point at it, otherwise you get NXDOMAIN down the chain. Create the delegated zone first, then add the CNAME.
When it is still broken
NXDOMAINpersists after adding the CNAME. Check for a stale cached negative answer (DNS caches "does not exist" too). Query the authoritative server directly withdig @nsto bypass caches, and confirm the CNAME has no typo or missing trailing dot in providers that require FQDNs.- The client writes the TXT but validation still fails. Confirm the TXT landed in the delegated zone at exactly
example.com.acme.your-dns.net, matching the CNAME target character for character. A mismatch between the CNAME target and where the client writes is the classic silent failure. - You are hitting rate limits from repeated failed attempts. That is a separate wall; see Let's Encrypt "too many certificates already issued" and test against the staging endpoint while you debug delegation.
- Renewals succeed manually but not from cron. The cron environment may lack the credentials file path or environment variables the DNS plugin needs. Run the renewal command with the exact cron environment to reproduce it.
The durable takeaway: DNS-01 validation is a question about authority, not timing. When it fails, do not lengthen the sleep; ask dig which server is authoritative for _acme-challenge and whether the record is actually there. A one-time CNAME delegation to a small automated zone answers that question permanently and keeps renewal credentials off your primary DNS for good.
Frequently asked questions
- Why does DNS-01 validation fail with NXDOMAIN when my TXT record clearly exists?
- Because Let's Encrypt reads the TXT record from the authoritative nameserver for _acme-challenge, and your record is on a different server than the one that is authoritative for that name. This happens when the zone is split across providers or a delegation is misconfigured. Increasing the propagation wait does nothing, because the record will never appear on the server the validator actually queries. Verify with dig against the authoritative nameserver, not your local resolver.
- How do I delegate _acme-challenge to a different DNS provider?
- Create a small zone you control with API access, for example acme.your-dns.net. Then add a one-time CNAME on your primary DNS: _acme-challenge.example.com CNAME example.com.acme.your-dns.net. Point your ACME client's DNS plugin at the delegated provider so it writes the TXT record there. Let's Encrypt follows the CNAME to the delegated zone and reads the record, and your primary DNS never handles automation.
- Does the _acme-challenge CNAME work for wildcard certificates?
- Yes. For a wildcard like *.example.com the challenge name is still _acme-challenge.example.com, so a single CNAME at that name covers both the wildcard and the apex domain. You only need additional CNAMEs for subdomains that are themselves separately delegated.
- How do I verify the delegation is set up correctly?
- Run dig +short CNAME _acme-challenge.example.com and confirm it points to your delegated target. Then find the authoritative nameserver for the delegated zone with dig NS, and query the TXT record directly against it: dig @that-nameserver TXT example.com.acme.your-dns.net. Querying the authority directly bypasses caches and reproduces exactly what Let's Encrypt does.