KB
DNS

DNS Troubleshooting: dig, Propagation and Common Failures

6 min read1223 words12 code blocks

At a glance#

  • Purpose: Diagnose DNS resolution problems methodically and identify where in the chain a lookup is failing.
  • Applies to: Any environment; examples use dig on Linux and nslookup on Windows.
  • Risk: None — all commands are read-only queries.
  • Time: 15–45 minutes depending on the fault.

Overview#

"The website is down" is a DNS problem more often than anything else. The difficulty is that DNS failures look identical from the outside regardless of cause — a missing record, a stale cache, a lame delegation and an expired domain all produce the same "site not loading" report.

This article gives a fixed diagnostic order. Work through it top to bottom and the failure isolates itself:

text
Registrar → Parent zone (delegation) → Authoritative NS → Recursive resolver → Client cache

Always test from the authoritative end downwards. Testing only from your laptop tells you what your resolver cached, not what is actually published.

Before you start#

  • The affected domain name.
  • Knowledge of which server should be authoritative for it.
  • dig installed (bind-utils on RHEL, dnsutils on Debian/Ubuntu).
Tip: Use dig +short for a quick answer and full dig when you need to see the reasoning. Add +trace when you do not yet know where the fault is.

The diagnostic sequence#

1. Confirm the domain is actually registered and not expired#

An expired domain removes the delegation entirely, and no amount of server-side work will fix it.

bash
whois example.com | grep -iE "expiry|expiration|status"

Look for Expiry Date in the past, or a status of clientHold, serverHold, pendingDelete or redemptionPeriod. Any of those means the registrar has suspended the domain — see Domain Registration, Renewal, DNS Delegation and Transfer.

2. Check the delegation at the parent zone#

This is what the internet believes your nameservers are, and it is frequently wrong after a migration.

bash
dig NS example.com @a.gtld-servers.net +norecurse

Compare that with what your own nameservers claim:

bash
dig NS example.com @ns1.example.com
Warning: If these two lists disagree, you have a lame delegation. Some resolvers will follow the parent and some will use cached data, so the domain works intermittently and from some networks but not others. This is the single most confusing DNS fault and the one most often misdiagnosed as "propagation".

Fix it at the registrar, not on the server.

3. Query the authoritative server directly#

Bypass every cache and ask the server that actually holds the zone:

bash
dig A example.com @ns1.example.com

Read the status: field in the header:

StatusMeaningWhere to look
NOERROR with an ANSWER sectionRecord exists and is being servedProblem is downstream — caching or client
NOERROR with ANSWER: 0Domain exists but this record type does notThe record is missing; add it
NXDOMAINThe name does not exist in the zone at allWrong zone loaded, or typo in the record
SERVFAILServer holds the zone but cannot answerZone failed to load, or DNSSEC validation failure
REFUSEDServer will not answer for this zoneNot authoritative, or ACL blocking the query

If the authoritative server does not have the record, stop here — nothing downstream matters.

4. Trace the full resolution path#

When the fault is not obvious, follow the delegation from the root:

bash
dig +trace example.com

This queries the root servers, then the TLD servers, then your nameservers, printing each hop. The step where it stops or diverges is the broken link.

5. Compare public resolvers#

If the authoritative answer is correct but users still fail, the problem is caching:

bash
dig +short A example.com @8.8.8.8      # Google
dig +short A example.com @1.1.1.1      # Cloudflare
dig +short A example.com @9.9.9.9      # Quad9

Different answers between resolvers means the change is still propagating. Identical wrong answers everywhere means the authoritative record is wrong.

Check how long the stale answer will persist:

bash
dig A example.com @8.8.8.8 | grep -A1 "ANSWER SECTION"

The number before the record class is the remaining TTL in seconds. Nothing will change until it reaches zero.

6. Check the client#

Rule out the local machine last:

bash
# Linux
resolvectl status
cat /etc/resolv.conf
resolvectl flush-caches

# Windows
ipconfig /all
ipconfig /displaydns
ipconfig /flushdns

Also check /etc/hosts — a stale entry there overrides DNS entirely and survives every cache flush.

Common record checks#

bash
# Mail routing
dig MX example.com +short

# SPF and other TXT records
dig TXT example.com +short

# DKIM - selector is provided by the mail platform
dig TXT selector._domainkey.example.com +short

# DMARC
dig TXT _dmarc.example.com +short

# Reverse DNS, needed for mail deliverability
dig -x 192.0.2.10 +short

# Nameservers as published by the zone itself
dig NS example.com +short

# SOA - useful for checking replication between primary and secondary
dig SOA example.com @ns1.example.com +short
dig SOA example.com @ns2.example.com +short
Note: If the SOA serial differs between your primary and secondary nameservers, zone transfer is broken. The secondary is serving stale data and will keep doing so until AXFR succeeds. See BIND9 / NAMED DNS Server – Complete Administration Guide.

Understanding propagation#

"Propagation" is misunderstood constantly. DNS does not push changes anywhere — resolvers pull records and hold them for the TTL. There is no global sync.

Consequences worth explaining to users:

  • A change is visible immediately to anyone who had not cached the old value.
  • Anyone holding the old record keeps it until their cached TTL expires.
  • The maximum wait is the old TTL, the one in effect before you made the change.
  • Lowering the TTL after making a change does nothing. It must be lowered beforehand.

Before any planned DNS change, lower the TTL to 300 seconds at least 24 hours ahead. Make the change, confirm it, then raise the TTL back to its normal value.

Verification#

Once the fault is fixed:

bash
# Authoritative servers agree
dig +short A example.com @ns1.example.com
dig +short A example.com @ns2.example.com

# Public resolvers agree
dig +short A example.com @8.8.8.8
dig +short A example.com @1.1.1.1

# Full path is clean
dig +trace example.com | tail -20

Then confirm the service itself, not just the record:

bash
curl -sI https://example.com | head -1

Troubleshooting#

SymptomLikely causeFix
SERVFAIL from your own nameserverZone file has a syntax error and failed to loadnamed-checkzone example.com /var/named/example.com.zone
SERVFAIL only from validating resolversDNSSEC records do not match the delegationCheck DS records at the registrar, or disable DNSSEC until fixed
REFUSEDServer not authoritative, or allow-query ACL blockingReview named.conf for that zone
Works internally, fails externallySplit-horizon DNS, or firewall blocking port 53 from outsideTest from an external host; check UDP and TCP 53
Works for some users onlyLame delegation, or mid-propagationCompare parent NS records with the zone's own
Record correct but site still downNot a DNS faultMove on to the web server or firewall
Intermittent resolutionOne of several nameservers is brokenQuery each NS individually
Mail bouncing, web fineMX, SPF or PTR problemCheck MX, TXT and reverse DNS separately