Skip to main content
RunBook Academy

LinuxXXII · Network TroubleshootingDNS

dig and nslookup - DNS diagnosis in detail

Foundation⏱ ~12 mindignslookuphostresolvectl

What you'll learn

  • Use dig to query specific record types and resolvers
  • Read dig output - status, answer, authority, additional
  • Trace DNS resolution with dig +trace
  • Diagnose DNS misconfiguration: stale caches, wrong resolvers, split horizon
  • Isolate DNS transport faults using the TC flag, EDNS buffer size, and TCP/53

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

DNS is invisible when it works and dominant when it does not. “Cannot reach host” is almost always “DNS cannot resolve host”. dig, nslookup, and host are the tools that turn a vague “DNS is broken” into a specific answer.

One thing to carry through this lesson: dig sends its query straight to a resolver over the wire. It does not use the /etc/nsswitch.conf path that your applications use, so it skips /etc/hosts, skips the search list, and skips whatever local stub resolver the host runs. That makes it the right tool for asking “what does the DNS actually say” and the wrong tool for asking “what will my application get”. Use getent hosts for the second question. Part XXIII covers the resolver architecture that produces the difference.

dig basics

dig example.com                   # A record (default)
dig example.com A                 # A record (explicit)
dig example.com AAAA              # IPv6 record
dig example.com MX                # mail server
dig example.com NS                # authoritative servers
dig example.com TXT               # SPF, DKIM, etc.
dig example.com CNAME             # canonical name
dig example.com ANY               # all records (often rate-limited)
dig -x 8.8.8.8                   # reverse lookup (PTR)
dig @8.8.8.8 example.com          # use a specific resolver
dig +short example.com            # just the answer
dig +trace example.com            # walk the resolution chain
dig +noall +answer example.com    # show only the answer section

The @8.8.8.8 syntax means “send this query to 8.8.8.8”, bypassing the system resolver. This is the diagnostic for “is the resolver broken or is the system misconfigured”.

Reading dig output

; <<>> DiG 9.18.24 <<>> example.com A
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;example.com.                   IN   A

;; ANSWER SECTION:
example.com.            300   IN   A    93.184.216.34

;; Query time: 12 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: ...
;; MSG SIZE  rcvd: 56

Key fields:

  • status: NOERROR (success), NXDOMAIN (name does not exist), SERVFAIL (server failure), REFUSED (policy refusal). Other statuses are rarely seen.
  • flags: qr = response (vs query). rd = recursion desired. ra = recursion available. aa = authoritative answer.
  • ANSWER SECTION: the records returned. Each line: name, TTL, class (IN), type, value.
  • TTL: how long the answer can be cached. Stale caches are a frequent cause of “DNS was working yesterday”.
  • SERVER: which resolver answered. 127.0.0.53 means systemd-resolved.
  • OPT PSEUDOSECTION / udp: 512: the EDNS buffer size this query advertised. It is the largest UDP response, in bytes, that the client says it is willing to receive. Anything bigger cannot come back over UDP intact. This one line explains a whole family of DNS incidents - see below.
  • MSG SIZE rcvd: how big the answer actually was. Compare it with the advertised buffer.

Common diagnostic queries

# Is this hostname resolvable at all?
dig example.com +short

# What does my resolver say?
dig @127.0.0.53 example.com     # systemd-resolved

# What does Google\'s public resolver say?
dig @8.8.8.8 example.com

# What does the authoritative server say?
dig @ns1.example.com example.com

# Trace the resolution from the root
dig +trace example.com

# Reverse lookup (IP to name)
dig -x 8.8.8.8 +short

# Check the SOA (start of authority) record
dig example.com SOA +noall +answer

# Check all nameservers for the zone
dig example.com NS +noall +answer

If the system resolver returns one answer and a public resolver returns another, you have a stale cache, a split horizon, or a misconfigured authoritative server.

DNS over TCP and truncation

DNS is not a UDP-only protocol. It has always had a TCP transport, and it uses it routinely.

The mechanism is simple. A query advertises how large a UDP response it can accept - that is the udp: 512 in the OPT pseudosection above. If the answer does not fit, the server returns a stub reply with the TC (truncated) flag set. The client then retries the same question over TCP port 53. dig prints ;; Truncated, retrying in TCP mode when this happens.

So the path from your host to a resolver has three requirements, not one:

  1. Small UDP responses must get through.
  2. Large UDP responses must get through - which often means fragmented UDP must survive the path.
  3. TCP/53 must be open.

Firewalls and middleboxes frequently satisfy the first and break one of the other two. The result is the most confusing DNS symptom there is: small answers work and large ones fail. A records resolve instantly, while DNSSEC-signed zones, long TXT/SPF records, or multi-record answers time out or SERVFAIL. Operators chase caches and resolver configuration for hours because “DNS works”.

The probe

Three queries isolate it. Run them against the same resolver, and change only the transport.

Read-only / Safeisolate the DNS transport
$ dig @10.0.0.1 example.com TXT +notcp +bufsize=512   # force UDP, small buffer
dig @10.0.0.1 example.com TXT +notcp +bufsize=4096  # force UDP, large buffer
dig @10.0.0.1 example.com TXT +tcp                  # force TCP/53

Read the results as a table:

UDP 512UDP 4096TCPConclusion
worksworksworksTransport is healthy. Look elsewhere.
works (TC set)fails/timeoutworksLarge or fragmented UDP is dropped in the path. The client falls back to TCP and recovers - slowly.
worksworksfails/timeoutTCP/53 is blocked by a firewall. Any answer needing fallback dies.
failsfailsworksUDP/53 is blocked entirely.

Two things to look for in the output:

  • flags: qr tc rd ra - the tc flag means the answer was truncated. With +notcp the answer you get back is incomplete.
  • ;; Truncated, retrying in TCP mode - dig telling you the fallback happened. If you see this on ordinary queries and TCP is slow, that is your latency.

nslookup

nslookup is older and less scriptable than dig. It is useful for interactive use because it has a prompt:

nslookup example.com
nslookup 8.8.8.8                # reverse
nslookup -type=MX example.com   # specific record type

In an interactive session:

> server 8.8.8.8
> set type=MX
> example.com

host

host is the simplest tool. It returns a one-line answer for most queries:

host example.com
host -t MX example.com
host 8.8.8.8                    # reverse

Useful in scripts and quick sanity checks.

resolvectl

resolvectl is the systemd-resolved diagnostic tool:

resolvectl status               # show all interfaces' DNS state
resolvectl statistics           # show cache hit/miss statistics
resolvectl flush-caches         # flush the cache
resolvectl query example.com    # query through systemd-resolved
resolvectl domain example.com   # show the search domain

resolvectl status is the first command to run on a host using systemd-resolved. It shows:

  • Which interfaces have which DNS configuration.
  • Whether systemd-resolved is in use.
  • The current cache statistics.

Common DNS failure modes

Stale cache: an answer was cached, but the authoritative record changed. Symptom: some hosts see the new IP, others see the old. Fix: resolvectl flush-caches or wait for the TTL.

Wrong resolver: /etc/resolv.conf points to a DNS server that cannot reach the authoritative zone. Symptom: NXDOMAIN or SERVFAIL for some names. Fix: change the resolver.

Split horizon: external and internal resolvers return different answers. Symptom: external hosts see one IP, internal hosts see another. Diagnose by querying both explicitly with @8.8.8.8 and @10.0.0.1.

DNSSEC validation failure: a chain of trust is broken. Symptom: SERVFAIL with ad flag not set. Fix: check DNSSEC chain, sometimes bypass with dig +cd (checking disabled).

Reverse DNS missing: an IP has no PTR record. Symptom: applications that require rDNS (mail servers, some firewalls) reject the connection.

Small answers resolve, large ones time out: A records are fine; DNSSEC-signed zones, long TXT/SPF records, or multi-record answers hang or SERVFAIL. Cause: TCP/53 is blocked, or large and fragmented UDP responses are dropped in the path. Diagnose by comparing dig +notcp +bufsize=4096 with dig +tcp as shown above. Fix: open TCP/53 both ways, or lower the advertised EDNS buffer on the resolver so it truncates and falls back earlier.

Knowledge check

Knowledge check · 5 questions

  1. Q1. What dig query shows the full resolution chain from the root?

  2. Q2. A dig SERVFAIL status usually means the name does not exist.

  3. Q3. Which of the following are DNS query tools on Linux? Select all that apply.

  4. Q4. A firewall that permits udp/53 but not tcp/53 leaves ordinary A record lookups working while large answers fail.

  5. Q5. Mail is failing SPF lookups from one site only. From that site, `dig example.com A` returns instantly, but `dig example.com TXT` times out. Both go to the same resolver. What do you run next, and what are you testing?

Passing score: 75%. Answers are checked in this browser.