Troubleshooting
Most DNS problems are one of three things: the answer is old (a cache), the answer is refused (a policy), or the answer fails validation (DNSSEC). Modern servers usually say which, in an Extended DNS Error (EDE) next to the response code, so read the full dig output before guessing.
A broken chain of trust
The most dangerous DNSSEC mistake is a wrong DS at the parent, typically after changing a zone's key without updating the registrar. In the lab, a DS that doesn't match example.net's key makes every validating resolver return SERVFAIL, with EDE 9: DNSKEY Missing. Your whole domain disappears for everyone who validates, while it still works for everyone who doesn't, which makes it confusing to diagnose.
The tell-tale test is dig +cd (checking disabled). It asks the resolver to skip validation: if +cd returns an answer and a plain query returns SERVFAIL, the problem is DNSSEC, not reachability.
Caches everywhere
The authoritative server caches too. When the DS was changed with pdnsutil, the lab root kept serving the old record from its packet cache until pdns_control purge cleared it. These defaults explain most "my change didn't take" moments:
| Setting | Default | Effect |
|---|---|---|
cache-ttl | 20 s | Answers served from the packet cache after a change |
query-cache-ttl / negquery-cache-ttl | 20 s / 60 s | Backend lookups (and misses) cached |
zone-cache-refresh-interval | 300 s | New zones from pdnsutil aren't served until then (or a restart) |
xfr-cycle-interval | 60 s | How often changed serials trigger NOTIFY |
On the recursor, rec_control wipe-cache example.net$ removes a zone's names from its cache. Changes made through the API clear the authoritative caches themselves.
Symptom, cause and fix
| Symptom | Cause | Fix |
|---|---|---|
| pdns won't start: no backends configured | Empty launch= (the Debian default) | Configure a backend in pdns.d/ |
| REFUSED for a zone you just created | The zone cache hasn't refreshed | Restart, wait 5 minutes, or create zones through the API |
| REFUSED for names you don't host | Authoritative servers don't recurse | Correct: point clients at the resolver |
| Secondary: Not Authorized; primary: client IP has no permission | Secondary not in allow-axfr-ips | Add it, then pdns_control retrieve on the secondary |
| Secondary has an old serial | No NOTIFY yet (60 s check), zone kind Native, or the serial wasn't bumped | Wait, pdns_control notify, set-kind primary, increase-serial |
secure-zone: No backend was able to secure | gsqlite3-dnssec not set | Set it on every server |
| SERVFAIL, EDE 22 No Reachable Authority | Resolver can't or won't reach the servers | Check routing and firewalls (in a lab, dont_query) |
| SERVFAIL, EDE 10 RRSIGs Missing for internal names | Unsigned internal zone under a signed public zone | Negative trust anchor, or sign and delegate it |
SERVFAIL, EDE 9 DNSKEY Missing; +cd works | Parent's DS doesn't match the zone's key | Publish the correct DS, then wait out the old one's TTL |
| Timeout from outside the office | Client not in the recursor's allow_from | Correct for outsiders; check the client's address |
root@rootns:~# pdnsutil replace-rrset net example DS 3600 '18425 13 2 0000000000000000000000000000000000000000000000000000000000000000' >/dev/null && pdnsutil rectify-zone net >/dev/null && pdns_control purge
Adding NSEC ordering information for zone 'net', 11 updates
23
root@recursor:~# rec_control wipe-cache example.net$
wiped 6 records, 0 negative records, 1 packets
root@client:~# dig www.example.net
; <<>> DiG 9.20.29-1~deb13u1-Debian <<>> www.example.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 44395
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; EDE: 9 (DNSKEY Missing)
;; QUESTION SECTION:
;www.example.net. IN A
;; Query time: 1 msec
;; SERVER: 10.10.0.53#53(10.10.0.53) (UDP)
;; WHEN: Wed Oct 21 05:47:07 UTC 2026
;; MSG SIZE rcvd: 50
root@client:~# dig www.example.net +cd +short
203.0.113.80
root@rootns:~# pdnsutil replace-rrset net example DS 3600 '18425 13 2 9b98e79540d98caeb43674c9a6530ecbaa8ea151d501fb8c99819c15154bdd92' >/dev/null && pdnsutil rectify-zone net >/dev/null && pdns_control purge
Adding NSEC ordering information for zone 'net', 11 updates
5
root@recursor:~# rec_control wipe-cache example.net$
wiped 6 records, 0 negative records, 2 packets
root@client:~# dig www.example.net | grep -E "status|flags"
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17324
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
; EDNS: version: 0, flags:; udp: 512- Lines 1–3: A DS that doesn't match example.net's key, then clear the root server's cache.
- Lines 5–6: Clear the recursor's cached copy of example.net.
- Line 13: SERVFAIL for a perfectly healthy zone…
- Line 18: …because the parent points at a key the zone doesn't have.
- Lines 28–29: With validation turned off (
+cd), the answer is there: the problem is DNSSEC. - Lines 31–40: The correct DS restored: validated again.
root@rootns:~# pdns_server --config=default 2>/dev/null | grep -E "^# (cache-ttl|negquery-cache-ttl|query-cache-ttl|zone-cache-refresh-interval|xfr-cycle-interval)="
# cache-ttl=20
# negquery-cache-ttl=60
# query-cache-ttl=20
# xfr-cycle-interval=60
# zone-cache-refresh-interval=300- Lines 2–6: Why changes made with pdnsutil can take a little while to appear.
Check your understanding
Users say example.net is down, but it works from your laptop, which doesn't validate DNSSEC. dig +cd through the office resolver returns the address; a plain query returns SERVFAIL. What's the most likely cause?
- A DNSSEC problem, such as a DS at the parent that no longer matches the zone's key.
- The authoritative servers are unreachable.
- The resolver's allow_from excludes the users.
- The secondary hasn't transferred the zone.