Two ACME Clients, One Domain: Handling Multiple _acme-challenge Records Safely
Two teams, one domain. One runs Certbot for the main website. Another runs acme.sh (or Traefik, or cert-manager) for an internal service on the same base domain. Both use DNS-01 validation, both write a TXT record at _acme-challenge.example.com, and for months everything is fine because they never renew at the same time. Then one week both renewals land together, and one of them fails with an authorization error that makes no sense, because the record "was definitely there".
The usual response is the worst possible one: someone logs in and manually deletes the "stray" TXT record right before their own validation, to make sure their value is the one that gets checked. That fixes their renewal and breaks the other team's, intermittently, forever. Now you have two clients fighting over one DNS name, each clobbering the other, and a certificate that renews only when the coin lands their way.
The fix rests on one fact most people do not know: a DNS name can hold many TXT records at once, and the ACME server is happy if any one of them matches. You almost never need to delete anything. You need your clients to add, not replace.
DNS allows multiple TXT records at the same name, and Let's Encrypt validates DNS-01 if any TXT record at _acme-challenge matches the expected value. So two ACME clients can coexist as long as each one adds its record and removes only its own afterwards, never wiping the whole name. The real conflict is CNAME delegation: a name can have only one CNAME, so you cannot delegate _acme-challenge to two different targets. Either share one delegation, or split the challenge per subdomain so each client owns a distinct _acme-challenge name.
The symptom: intermittent DNS-01 authorization failures
The failure looks like the challenge value is simply not there, even though you can see a TXT record when you check:
Error finalizing order :: authorization must be complete
Detail: DNS problem: TXT record for _acme-challenge.example.com
does not match the expected value; no TXT record found matching the challenge token
The tell is the word intermittent. A misconfiguration fails every time. This fails only when two renewals overlap, or only for whichever client validates second, because by then the first client's cleanup step has removed the record the second client was relying on, or a "replace" write overwrote it.
Why this happens: TXT sets, and the one-CNAME rule
Two independent DNS facts collide here, and knowing both dissolves the whole problem.
Fact one: a name can hold multiple TXT records. DNS stores records as a set at each name and type. _acme-challenge.example.com can hold several TXT values simultaneously, and a resolver returns all of them. The ACME server, when it validates a DNS-01 challenge, looks at every TXT record at that name and accepts the authorization if any one of them equals the expected token. So two clients writing two different tokens at the same name do not inherently conflict. Both tokens can sit there, and each client's validation finds its own. This is by design; it is also how a single client validates a certificate covering both example.com and *.example.com, which need two tokens at the same _acme-challenge name at once.
Fact two: a name can hold only one CNAME. The DNS specification forbids a CNAME from coexisting with other records at the same name, which in practice means a name resolves through exactly one CNAME target. If both teams try to CNAME _acme-challenge.example.com to their own delegation zone, they cannot. There is one CNAME slot and they are fighting over it. This, not the TXT records, is the genuine structural conflict.
The fix, step by step
Step 1: Confirm your clients add rather than replace
The root cause is almost always a DNS-01 hook that assumes it owns the record and overwrites the whole TXT set. Check what each client's hook does on the "present" step. A safe hook uses the DNS provider's API to append a TXT value; an unsafe one does a "set records to [my value]" call that wipes anything else there. Most maintained plugins (Certbot's dns-* plugins, acme.sh's DNS API hooks, cert-manager's issuers) append correctly. Custom shell hooks written years ago are the usual culprit.
If you control the hook, make the present step additive and the cleanup step delete only the specific value it added, matched by content, not "delete all TXT at this name":
# cleanup: remove ONLY the record this run created, by its value
# pseudo-API: never "delete all TXT at _acme-challenge"
provider_dns delete-txt \
--name "_acme-challenge.example.com" \
--value "$CERTBOT_VALIDATION"
Step 2: Verify multiple records can live at the name
Before relying on coexistence, prove your DNS provider actually keeps both. Write two test TXT records and query them:
dig +short TXT _acme-challenge.example.com @1.1.1.1
Expected output when two clients have both written: two quoted strings returned together.
"first-client-token-value"
"second-client-token-value"
If you only ever see one, your provider or your hook is collapsing the set, and that is the bug. Fix the hook (Step 1) or the provider setting before going further.
Step 3: If you use CNAME delegation, pick one strategy
CNAME delegation for _acme-challenge is a great pattern (it lets your ACME client write into a small dedicated zone without API access to your main DNS), but only one CNAME can exist per name. If two clients need delegation on the same FQDN, you have two clean options:
- Share one delegation target. Both clients write into the same delegated zone, which itself holds a TXT set. This works because the delegated zone follows the same "multiple TXT records coexist" rule.
- Split by subdomain so the names differ. Give each client its own hostname so their challenge names never collide. Client A validates
www.example.com(challenge at_acme-challenge.www.example.com); client B validatesinternal.example.com(challenge at_acme-challenge.internal.example.com). Different names, no shared slot, no fight. Each can even CNAME-delegate independently.
Step 4: Assign a single automation owner
Even with additive hooks, two teams writing to one production zone is a coordination hazard. The durable fix is organisational: one owner for ACME automation on a given domain, or a clear split of responsibility per subdomain that matches Step 3. Write it down. The manual-deletion habit comes back the instant nobody owns the boundary.
Verification
Force both renewals close together in staging and confirm both succeed without either deleting the other's record. Use the Let's Encrypt staging environment so you do not burn rate limits, as covered in defaulting Certbot to the Let's Encrypt staging server:
# client A, dry run against staging
certbot renew --dry-run
# client B, force a renewal against staging
acme.sh --renew -d internal.example.com --staging --force
During the window, query the challenge name and you should see both tokens present at once:
watch -n 2 'dig +short TXT _acme-challenge.example.com @1.1.1.1'
Both dry runs completing successfully, with two TXT values visible together during the overlap, proves the clients coexist. If one still fails, its cleanup step is deleting more than its own record.
What people get wrong
Manually deleting the "other" TXT record. This is the dangerous shortcut this whole article exists to kill. It appears to fix your renewal because now only your token is present, but you have removed the record the other client's next validation depended on. Their renewal fails on their schedule, someone deletes yours in retaliation, and you have built a race condition into your certificate infrastructure. Never delete a TXT record you did not create.
Assuming a name holds only one TXT record. The mental model of "one record per name" is wrong for TXT, and it is the false assumption behind the deletion habit. TXT is a set. A wildcard-plus-apex certificate from a single client already relies on two tokens coexisting at one name.
CNAME-delegating the same name twice. Two teams both trying to CNAME _acme-challenge.example.com to their own zones will find only one CNAME survives, because a name can hold exactly one. Split by subdomain or share the delegation target; do not fight over the slot.
Blaming rate limits. Overlapping clients can also trip issuance limits, which sends people down the wrong path. If the error is genuinely about too many certificates rather than a missing TXT record, that is a different problem entirely; see Let's Encrypt too many certificates already issued.
When it is still broken
- Only one TXT ever appears. Your DNS provider's API or plugin does a full replace on write. Check for an "append" or "add record" mode, or move that client to a delegated zone it fully owns.
- DNS propagation lag. If validation runs before the new TXT record has propagated to the authoritative servers Let's Encrypt queries, it fails regardless of coexistence. Increase the client's propagation wait, or confirm with
digagainst the authoritative nameserver before letting validation proceed. - Split-horizon or hidden secondary DNS. If an internal resolver serves a different view than the public one Let's Encrypt sees, your
digfrom inside the network can lie. Always verify against a public resolver like@1.1.1.1. - A stale cleanup deletes late. A client whose cleanup runs on a delay can delete a record after the other client wrote a new one, if it matches by name instead of value. Ensure every cleanup deletes strictly by the exact value it created.
Frequently asked questions
- Can two ACME clients use DNS-01 on the same domain?
- Yes. A DNS name can hold multiple TXT records, and Let's Encrypt validates DNS-01 if any TXT record at _acme-challenge matches the expected token. As long as each client adds its own record and removes only that record afterwards, both clients coexist. The problem is only when a client replaces the whole TXT set.
- Why do my Let's Encrypt renewals fail only sometimes?
- Almost always because two ACME clients share the _acme-challenge name and one client's hook overwrites or deletes the other's TXT record. It fails only when renewals overlap, or for whichever client validates second. Make each client's DNS hook append its record and clean up only its own value by content.
- Should I delete the other TXT record before my ACME validation?
- No. That is the most common and most damaging mistake. Deleting the other client's record makes your renewal pass but breaks theirs on their next run, creating a race condition in your certificate infrastructure. DNS holds many TXT records at once; leave records you did not create alone.
- Can I CNAME _acme-challenge to two different delegation zones?
- No. A DNS name can hold only one CNAME, so two delegations on the same _acme-challenge name conflict. Either point both clients at a single shared delegation zone, or split the challenge by subdomain so each client validates a different hostname with its own distinct _acme-challenge name.