cheat sheet

dig

Query DNS records of any type from any resolver — A, AAAA, MX, TXT, NS, SOA, SRV, CAA — with formatted output, reverse lookups, and full delegation tracing.

dig — DNS Lookup Toolkit

What it is

dig (Domain Information Groper) is the canonical command-line DNS query tool maintained by the Internet Systems Consortium (ISC) as part of the BIND distribution. It sends a single DNS query to a resolver and prints the full response — header flags, question, answer, authority, and additional sections — making it the standard tool for debugging DNS propagation, validating record content, and tracing delegation. Reach for dig when you need an authoritative view of what DNS is actually returning; nslookup is the older interactive cousin (still useful for quick lookups, and the default on Windows) and host is a one-line wrapper for simple A/AAAA/MX questions.

Install

dig ships in the dnsutils (Debian/Ubuntu) or bind-utils (RHEL/Fedora) package. macOS includes it with the system, and Homebrew offers a newer bind for an up-to-date version. ISC maintains two current branches: 9.20 (stable LTS, supported through Q1 2028) and 9.21 (experimental development branch); the older 9.18 stream is still receiving security patches. The 9.20 line completed the libuv asynchronous event-loop rewrite started in 9.18 — relevant chiefly for named, but it also tightens timing in dig's own networking path.

bash
# Debian/Ubuntu
sudo apt install dnsutils

# RHEL/Fedora
sudo dnf install bind-utils

# macOS (built-in; or newer via Homebrew)
brew install bind

# Verify
dig -v

Output:

text
DiG 9.20.15

Syntax

The general form is dig [@server] [type] name [+query-options]. Order matters loosely — @server selects the resolver, type defaults to A, and +options toggle output formatting and protocol behaviour.

bash
dig [@SERVER] [TYPE] NAME [+OPT...]
dig -x IPADDR              # reverse lookup
dig -f QUERYFILE           # batch mode (one query per line)
dig -p PORT @SERVER NAME   # non-standard port (useful for local resolvers)

Output: (none — exits 0 on success)

Essential options

OptionMeaning
@serverSend the query to this resolver (IP or hostname)
-t TYPERecord type (also positional: dig example.com MX)
-x IPReverse DNS — query PTR via in-addr.arpa / ip6.arpa
-p PORTUse non-standard UDP/TCP port
-4 / -6Force IPv4 or IPv6 transport
+shortPrint only the answer values, one per line
+noall +answerPrint only the ANSWER section, full RR format
+traceTrace delegation from the root down
+nssearchQuery every authoritative NS and show the SOA
+tcp / +notcpForce TCP / force UDP
+norecurseDisable recursion — query an authoritative server directly
+dnssecRequest DNSSEC records (RRSIG, NSEC, DS)
+stats / +nostatsToggle the trailing statistics block
+time=NPer-query timeout in seconds (default 5)
+tries=NTotal attempts before failure (default 3)

A basic query

A bare dig example.com returns an A record for the name using the system resolver in /etc/resolv.conf. The output has five blocks: the header, question, answer, authority, and a stats footer.

bash
dig example.com

Output:

text
; <<>> DiG 9.20.15 <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4231
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

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

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

;; Query time: 12 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Sun May 24 14:00:00 UTC 2026
;; MSG SIZE  rcvd: 56

Compact output formats

+short strips everything except the answer values and is ideal for scripts. +noall +answer keeps the full resource-record format (TTL, class, type) but removes the noisy header and stats. Combine multiple +no... flags to mute sections.

bash
dig +short example.com
dig +noall +answer example.com
dig +noall +answer +multiline example.com TXT   # split long TXT records nicely

Output:

text
# +short
93.184.216.34

# +noall +answer
example.com.           86400   IN      A       93.184.216.34

# TXT with +multiline
example.com.           300 IN  TXT     ("v=spf1 -all"
                                       " google-site-verification=abcdef"
                                       )

Record types

DNS records carry different payloads — IPv4 addresses (A), IPv6 (AAAA), mail routing (MX), text (TXT), nameservers (NS), zone metadata (SOA), service location (SRV), and CA authorization (CAA). Pass the type as the third positional argument or with -t.

bash
dig example.com A         # IPv4
dig example.com AAAA      # IPv6
dig example.com MX        # mail exchangers
dig example.com NS        # nameservers
dig example.com TXT       # SPF, DKIM, verification, etc.
dig example.com SOA       # zone authority + serial
dig _sip._tcp.example.com SRV
dig example.com CAA       # which CAs may issue certs
dig example.com ANY       # may be refused by modern resolvers (RFC 8482)

Output:

text
# MX
example.com.           300 IN  MX  10 mx1.example.com.
example.com.           300 IN  MX  20 mx2.example.com.

# NS
example.com.           172800 IN NS  a.iana-servers.net.
example.com.           172800 IN NS  b.iana-servers.net.

# SOA
example.com.           3600 IN SOA ns.icann.org. noc.dns.icann.org. (
                            2024010101 ; serial
                            7200       ; refresh
                            3600       ; retry
                            1209600    ; expire
                            3600 )     ; minimum

# CAA
example.com.           300 IN  CAA 0 issue "letsencrypt.org"
example.com.           300 IN  CAA 0 iodef "mailto:security@example.com"

Choosing a resolver

Prefix the query with @host to bypass /etc/resolv.conf and query a specific resolver. Cross-checking against several resolvers is the standard way to verify DNS propagation after a record change.

bash
dig @1.1.1.1     example.com           # Cloudflare
dig @8.8.8.8     example.com           # Google
dig @9.9.9.9     example.com           # Quad9 (filters malicious)
dig @208.67.222.222 example.com        # OpenDNS
dig @ns.icann.org example.com SOA      # ask an authoritative server directly

# Compare answers across resolvers in one line
for r in 1.1.1.1 8.8.8.8 9.9.9.9; do
  printf '%-12s ' "$r"; dig +short @"$r" example.com
done

Output:

text
1.1.1.1      93.184.216.34
8.8.8.8      93.184.216.34
9.9.9.9      93.184.216.34

Reverse lookups

-x builds the correct in-addr.arpa (IPv4) or ip6.arpa (IPv6) PTR query for you. PTR records are typically owned by the network operator, not the domain holder, so they often disagree with the forward (A/AAAA) record.

bash
dig -x 93.184.216.34            # IPv4 PTR
dig -x 2606:2800:220:1:248:1893:25c8:1946   # IPv6 PTR
dig +short -x 1.1.1.1

Output:

text
# dig +short -x 1.1.1.1
one.one.one.one.

# dig -x 93.184.216.34  (answer section)
34.216.184.93.in-addr.arpa. 21600 IN  PTR  example.com.

Tracing the delegation chain

+trace walks DNS from the root (.) servers down through the TLD and finally the zone's own authoritative servers, printing each step. This is the definitive way to diagnose delegation problems (wrong NS at the parent, missing glue, stale registrar data).

bash
dig +trace example.com
dig +trace +nodnssec example.com    # quieter — drop DNSSEC RRs

Output:

text
; <<>> DiG 9.20.15 <<>> +trace example.com
;; global options: +cmd
.                       518400  IN      NS      a.root-servers.net.
.                       518400  IN      NS      b.root-servers.net.
...
;; Received 525 bytes from 1.1.1.1#53(1.1.1.1) in 8 ms

com.                    172800  IN      NS      a.gtld-servers.net.
com.                    172800  IN      NS      b.gtld-servers.net.
...
;; Received 1186 bytes from 198.41.0.4#53(a.root-servers.net) in 24 ms

example.com.            172800  IN      NS      a.iana-servers.net.
example.com.            172800  IN      NS      b.iana-servers.net.
;; Received 270 bytes from 192.5.6.30#53(a.gtld-servers.net) in 32 ms

example.com.            86400   IN      A       93.184.216.34
;; Received 56 bytes from 199.43.135.53#53(a.iana-servers.net) in 18 ms

Asking authoritative servers directly

When you want the true zone content (bypassing any caching), look up the authoritative nameservers from the SOA and query them with +norecurse. Anything they return is what their zone file actually says.

bash
# Discover the auth servers
dig +short NS example.com

# Ask each authoritative server directly, no recursion
for ns in $(dig +short NS example.com); do
  printf '%-25s ' "$ns"
  dig +short +norecurse @"$ns" example.com
done

# Convenience: query every NS at once
dig +nssearch example.com

Output:

text
a.iana-servers.net.       93.184.216.34
b.iana-servers.net.       93.184.216.34

# +nssearch
SOA ns.icann.org. noc.dns.icann.org. 2024010101 7200 3600 1209600 3600 from server 199.43.135.53 in 17 ms.
SOA ns.icann.org. noc.dns.icann.org. 2024010101 7200 3600 1209600 3600 from server 199.43.133.53 in 21 ms.

Reading the header flags

The flags: line in the header tells you what kind of answer you got. Match each flag to its meaning when debugging unexpected behaviour.

FlagMeaning
qrQuery response (set on every reply)
aaAuthoritative answer — came from the zone's own server
tcTruncated — answer didn't fit in UDP; retry with TCP
rdRecursion desired — client asked the server to recurse
raRecursion available — server is willing to recurse
adAuthenticated data — DNSSEC validated
cdChecking disabled — client asked to skip DNSSEC validation
bash
dig example.com                       # rd ra → recursive resolver answered
dig +norecurse @a.iana-servers.net example.com   # aa → authoritative answer

Output:

text
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 3

DNSSEC

+dnssec sets the DO (DNSSEC OK) bit and returns the signing records (RRSIG, NSEC, DS). If the resolver validates the chain, the answer will carry the ad (Authenticated Data) flag.

bash
dig +dnssec example.com
dig +sigchase example.com           # old; deprecated in newer BIND
dig DS example.com                  # delegation signer at the parent
dig DNSKEY example.com

Output:

text
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
example.com.    86400   IN  A       93.184.216.34
example.com.    86400   IN  RRSIG   A 13 2 86400 20260601000000 (
                                20260510000000 12345 example.com.
                                abcdEFGhij...== )

DoH, DoT, and DoQ

Modern dig (9.18+) speaks DNS-over-TLS (+tls, port 853), DNS-over-HTTPS (+https, port 443), and — in 9.20 — DNS-over-QUIC (+quic, port 853). Use these to test resolvers that publish encrypted endpoints; the same @host syntax applies. DoT is easy to identify and filter on the network (distinct port), while DoH blends into ordinary HTTPS traffic — pick +tls when an admin needs to inspect traffic and +https when privacy from middleboxes matters. DoQ trades TCP head-of-line blocking for QUIC streams and is the newest of the three.

bash
# DNS-over-TLS (port 853)
dig +tls @1.1.1.1 example.com

# DNS-over-HTTPS (port 443) — explicit endpoint with +https-get / +https-post
dig +https @1.1.1.1 example.com
dig +https=/dns-query @cloudflare-dns.com example.com

# DNS-over-QUIC (port 853, UDP)
dig +quic @1.1.1.1 example.com

# Force TLS hostname verification against a specific SNI
dig +tls +tls-hostname=cloudflare-dns.com @1.1.1.1 example.com

Output:

text
;; SERVER: 1.1.1.1#853(1.1.1.1) (TLS)
;; ANSWER SECTION:
example.com.    86400   IN  A   93.184.216.34

Extended DNS Errors (EDE)

RFC 8914 defines machine-readable error codes that appear in the OPT pseudo-section when a resolver wants to explain why a query failed. BIND 9.20+ emits these for DNSSEC failures, cached SERVFAILs (EDE 13), missing DNSKEY records (EDE 9), filtered responses, stale data, and more. Use +ednsopt if you want to send a specific option, and read the OPT block in the response to see what came back.

bash
# Trigger a DNSSEC failure to see EDE in action
dig @1.1.1.1 dnssec-failed.org +dnssec

# Force a query that will likely SERVFAIL and show the cached-error code
dig @8.8.8.8 servfail.nask.waw.pl

Output:

text
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
; EDE: 6 (DNSSEC Bogus): (no RRSIGs from signer)
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 12345

Batch mode

-f reads one query per line from a file — useful for bulk checks (validating a list of hostnames, mass MX lookups, etc.).

bash
cat > queries.txt <<EOF
example.com A
example.com MX
example.org NS
alicedev.example.com TXT
EOF

dig -f queries.txt +noall +answer

Output:

text
example.com.        86400 IN  A   93.184.216.34
example.com.        300   IN  MX  10 mx1.example.com.
example.org.        172800 IN NS  a.iana-servers.net.
alicedev.example.com. 300 IN  TXT "owner=alice@example.com"

nslookup and host — quick comparison

nslookup is the older interactive utility — handy on Windows where it's the default. host is a one-shot wrapper that prints the answer in plain English. Both are fine for casual lookups; reach for dig whenever you need precise control or full output.

bash
# nslookup — same A lookup
nslookup example.com 1.1.1.1

# host — terse one-line output
host example.com
host -t MX example.com
host -t TXT example.com 8.8.8.8

# Reverse with host
host 93.184.216.34

Output:

text
# nslookup
Server:     1.1.1.1
Address:    1.1.1.1#53

Non-authoritative answer:
Name:   example.com
Address: 93.184.216.34

# host
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946

# host -t MX
example.com mail is handled by 10 mx1.example.com.
example.com mail is handled by 20 mx2.example.com.

# host 93.184.216.34
34.216.184.93.in-addr.arpa domain name pointer example.com.
ToolStrengthsWeaknesses
digFull output, all record types, scriptable, +trace, DoT/DoHVerbose by default
hostOne-line answer, easy to rememberLimited record-type formatting
nslookupCross-platform, interactive > promptConsidered legacy on Unix; weaker output

Configuration

dig reads ~/.digrc at startup and prepends its contents to every command line. The file is plain text — one option per line or all on one line — with the same syntax you'd type at the shell. Use it to default to a compact format, a preferred resolver, or always-on flags like +dnssec.

text
# ~/.digrc
+noall
+answer
+nostats
@1.1.1.1

Output: (none — exits 0 on success)

bash
# After the rc above, `dig example.com` is equivalent to:
# dig +noall +answer +nostats @1.1.1.1 example.com

Output: (none — exits 0 on success)

/etc/resolv.conf still controls the system resolver, search-domain list, and per-query timeouts when no @server is given. The two files are independent — ~/.digrc only affects dig.

Modern alternatives

Several newer DNS clients improve on dig's ergonomics — colorized output, native JSON, and first-class DoH/DoT/DoQ flags — while keeping a dig-compatible argument style. They're worth keeping in your toolbox; dig remains the lingua franca for tutorials and bug reports.

ToolLanguageNotable
doggoGoDoH/DoT/DoQ/DNSCrypt out of the box, --short, --json, color tables; brew install doggo
dogRustColorized output, --json, DoH/DoT; no DoQ; brew install dog
drillC (ldns)Ships in many LDNS packages; dig-like flags, terser output
kdigC (Knot)From the Knot DNS project; the reference client for DoQ and modern EDNS features
bash
# Equivalent A lookups across tools
dig    +short example.com
doggo  example.com --short
dog    -1 example.com
drill  example.com
kdig   +short example.com

# Encrypted lookups
doggo example.com @https://1.1.1.1/dns-query
kdig  -d @1.1.1.1 +tls example.com

Output:

text
93.184.216.34

Exit codes

CodeMeaning
0Success — query returned (even if NXDOMAIN)
1Usage error
8Couldn't open batch file (-f)
9No reply from server
10Internal error
bash
if dig +short @1.1.1.1 example.com | grep -q .; then
  echo "Got at least one answer"
fi

Output: (none — exits 0 on success)

Common pitfalls

  1. Forgetting that resolvers cache. A record change won't appear until the TTL expires or the cache is flushed. Query the authoritative server with +norecurse to see the live zone.
  2. ANY queries are unreliable. Many resolvers and authoritative servers refuse ANY (RFC 8482) and return HINFO RFC8482. Query each type explicitly.
  3. Trailing dot. example.com. (with dot) is a fully qualified name. Without the dot, dig may append your search domain from /etc/resolv.conf and look up example.com.mydomain.local instead.
  4. CNAME chains hide A records. A query for www.example.com A may return a CNAME first; use +short to see the full chain.
  5. TCP truncation. Large answers (DNSSEC, many MX records) may be truncated over UDP (tc flag). Modern dig retries on TCP automatically, but a misconfigured firewall can drop port 53/TCP.
  6. PTR ≠ A. Reverse records are controlled by the network owner. The PTR for an IP rarely matches the A record exactly.

Real-world recipes

Verify DNS propagation across resolvers

After updating an A record at the registrar, check a handful of geographically distinct resolvers — if any still return the old IP, the change is mid-propagation.

bash
RESOLVERS="1.1.1.1 8.8.8.8 9.9.9.9 208.67.222.222 64.6.64.6"
HOST="example.com"
for r in $RESOLVERS; do
  printf '%-18s ' "$r"
  dig +short +time=2 +tries=1 @"$r" "$HOST" | head -1
done

Output:

text
1.1.1.1            203.0.113.10
8.8.8.8            203.0.113.10
9.9.9.9            203.0.113.10
208.67.222.222     192.0.2.45         <-- stale cache
64.6.64.6          203.0.113.10

Find every authoritative nameserver for a zone

bash
dig +short NS example.com | sort

Output:

text
a.iana-servers.net.
b.iana-servers.net.

Pull SPF, DKIM, and DMARC for an email domain

Email auth records all live in TXT; their names are predictable. This one-liner pulls all three in one shot.

bash
DOMAIN=example.com
echo "--- SPF ---";    dig +short TXT "$DOMAIN"            | grep spf1
echo "--- DKIM ---";   dig +short TXT "default._domainkey.$DOMAIN"
echo "--- DMARC ---";  dig +short TXT "_dmarc.$DOMAIN"

Output:

text
--- SPF ---
"v=spf1 include:_spf.google.com -all"
--- DKIM ---
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCg..."
--- DMARC ---
"v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

Look up the SOA serial across all auth servers

A mismatched SOA serial means one or more nameservers haven't received the latest zone update yet.

bash
DOMAIN=example.com
for ns in $(dig +short NS "$DOMAIN"); do
  serial=$(dig +short SOA @"$ns" "$DOMAIN" | awk '{print $3}')
  printf '%-30s serial=%s\n' "$ns" "$serial"
done

Output:

text
a.iana-servers.net.            serial=2024010101
b.iana-servers.net.            serial=2024010101

Check whether DNSSEC validates

bash
dig +dnssec +short example.com | head
dig example.com | grep -q ' ad ' && echo "DNSSEC: validated" || echo "DNSSEC: not validated"

Output:

text
93.184.216.34
A 13 2 86400 20260601000000 20260510000000 12345 example.com. abcd...
DNSSEC: validated

Reverse-lookup a netblock

Pair dig -x with a loop over a /29 to find which IPs in a small block have PTR records.

bash
for n in $(seq 0 7); do
  ip="203.0.113.$n"
  ptr=$(dig +short -x "$ip")
  printf '%-15s %s\n' "$ip" "${ptr:-(none)}"
done

Output:

text
203.0.113.0     (none)
203.0.113.1     gw.example.com.
203.0.113.2     mail.example.com.
203.0.113.3     www.example.com.
203.0.113.4     (none)
203.0.113.5     (none)
203.0.113.6     vpn.example.com.
203.0.113.7     (none)

One-liner: every record for a host

Loop over the common types when you want a full picture without ANY.

bash
HOST=example.com
for t in A AAAA NS MX TXT SOA CAA; do
  echo "=== $t ==="
  dig +noall +answer "$HOST" "$t"
done

Output:

text
=== A ===
example.com.    86400 IN A   93.184.216.34
=== AAAA ===
example.com.    86400 IN AAAA  2606:2800:220:1:248:1893:25c8:1946
=== NS ===
example.com.    172800 IN NS  a.iana-servers.net.
example.com.    172800 IN NS  b.iana-servers.net.
=== MX ===
example.com.    300 IN  MX  10 mx1.example.com.
=== TXT ===
example.com.    300 IN  TXT "v=spf1 -all"
=== SOA ===
example.com.    3600 IN SOA ns.icann.org. noc.dns.icann.org. 2024010101 7200 3600 1209600 3600
=== CAA ===
example.com.    300 IN  CAA 0 issue "letsencrypt.org"

Watch a TTL count down

The TTL column in an answer is the remaining time-to-live in the resolver cache. Re-querying shows it shrinking until the resolver fetches a fresh copy.

bash
while :; do
  dig +noall +answer example.com | awk '{print strftime("%H:%M:%S"), "TTL", $2}'
  sleep 10
done

Output:

text
14:00:00 TTL 86400
14:00:10 TTL 86390
14:00:20 TTL 86380

Add +stats back when debugging — the trailing ;; Query time: N msec and ;; SERVER: lines tell you which resolver actually answered and how long it took. The ~/.digrc example above mutes them; comment that line out temporarily when something looks off.

dig +short ANY example.com will likely return nothing useful on a modern resolver. To enumerate records, loop over the explicit types you care about as shown in the "every record for a host" recipe.

Sources