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.
# 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:
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.
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
| Option | Meaning |
|---|---|
@server | Send the query to this resolver (IP or hostname) |
-t TYPE | Record type (also positional: dig example.com MX) |
-x IP | Reverse DNS — query PTR via in-addr.arpa / ip6.arpa |
-p PORT | Use non-standard UDP/TCP port |
-4 / -6 | Force IPv4 or IPv6 transport |
+short | Print only the answer values, one per line |
+noall +answer | Print only the ANSWER section, full RR format |
+trace | Trace delegation from the root down |
+nssearch | Query every authoritative NS and show the SOA |
+tcp / +notcp | Force TCP / force UDP |
+norecurse | Disable recursion — query an authoritative server directly |
+dnssec | Request DNSSEC records (RRSIG, NSEC, DS) |
+stats / +nostats | Toggle the trailing statistics block |
+time=N | Per-query timeout in seconds (default 5) |
+tries=N | Total 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.
dig example.com
Output:
; <<>> 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.
dig +short example.com
dig +noall +answer example.com
dig +noall +answer +multiline example.com TXT # split long TXT records nicely
Output:
# +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.
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:
# 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.
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:
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.
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:
# 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).
dig +trace example.com
dig +trace +nodnssec example.com # quieter — drop DNSSEC RRs
Output:
; <<>> 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.
# 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:
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.
| Flag | Meaning |
|---|---|
qr | Query response (set on every reply) |
aa | Authoritative answer — came from the zone's own server |
tc | Truncated — answer didn't fit in UDP; retry with TCP |
rd | Recursion desired — client asked the server to recurse |
ra | Recursion available — server is willing to recurse |
ad | Authenticated data — DNSSEC validated |
cd | Checking disabled — client asked to skip DNSSEC validation |
dig example.com # rd ra → recursive resolver answered
dig +norecurse @a.iana-servers.net example.com # aa → authoritative answer
Output:
;; 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.
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:
;; 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.
# 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:
;; 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.
# 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:
;; 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.).
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:
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.
# 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:
# 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.
| Tool | Strengths | Weaknesses |
|---|---|---|
dig | Full output, all record types, scriptable, +trace, DoT/DoH | Verbose by default |
host | One-line answer, easy to remember | Limited record-type formatting |
nslookup | Cross-platform, interactive > prompt | Considered 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.
# ~/.digrc
+noall
+answer
+nostats
@1.1.1.1
Output: (none — exits 0 on success)
# 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.
| Tool | Language | Notable |
|---|---|---|
doggo | Go | DoH/DoT/DoQ/DNSCrypt out of the box, --short, --json, color tables; brew install doggo |
dog | Rust | Colorized output, --json, DoH/DoT; no DoQ; brew install dog |
drill | C (ldns) | Ships in many LDNS packages; dig-like flags, terser output |
kdig | C (Knot) | From the Knot DNS project; the reference client for DoQ and modern EDNS features |
# 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:
93.184.216.34
Exit codes
| Code | Meaning |
|---|---|
0 | Success — query returned (even if NXDOMAIN) |
1 | Usage error |
8 | Couldn't open batch file (-f) |
9 | No reply from server |
10 | Internal error |
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
- Forgetting that resolvers cache. A record change won't appear until the TTL expires or the cache is flushed. Query the authoritative server with
+norecurseto see the live zone. ANYqueries are unreliable. Many resolvers and authoritative servers refuseANY(RFC 8482) and returnHINFO RFC8482. Query each type explicitly.- Trailing dot.
example.com.(with dot) is a fully qualified name. Without the dot,digmay append your search domain from/etc/resolv.confand look upexample.com.mydomain.localinstead. - CNAME chains hide A records. A query for
www.example.com Amay return a CNAME first; use+shortto see the full chain. - TCP truncation. Large answers (DNSSEC, many MX records) may be truncated over UDP (
tcflag). Moderndigretries on TCP automatically, but a misconfigured firewall can drop port 53/TCP. - 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.
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:
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
dig +short NS example.com | sort
Output:
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.
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:
--- 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.
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:
a.iana-servers.net. serial=2024010101
b.iana-servers.net. serial=2024010101
Check whether DNSSEC validates
dig +dnssec +short example.com | head
dig example.com | grep -q ' ad ' && echo "DNSSEC: validated" || echo "DNSSEC: not validated"
Output:
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.
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:
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.
HOST=example.com
for t in A AAAA NS MX TXT SOA CAA; do
echo "=== $t ==="
dig +noall +answer "$HOST" "$t"
done
Output:
=== 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.
while :; do
dig +noall +answer example.com | awk '{print strftime("%H:%M:%S"), "TTL", $2}'
sleep 10
done
Output:
14:00:00 TTL 86400
14:00:10 TTL 86390
14:00:20 TTL 86380
Add
+statsback when debugging — the trailing;; Query time: N msecand;; SERVER:lines tell you which resolver actually answered and how long it took. The~/.digrcexample above mutes them; comment that line out temporarily when something looks off.
dig +short ANY example.comwill 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
- BIND 9 release notes (stable)
- BIND 9 changelog
- BIND 9.20 — Streamlined Core, New Features (ISC blog)
- Recent BIND announcement: 9.18.41, 9.20.15, 9.21.14
- doggo — Command-line DNS Client (GitHub)
- dog — A user-friendly DNS client (Terminal Trove)
- DNS over TLS vs DNS over HTTPS (Cloudflare Learning)
- Encrypted DNS in 2026: DoH, DoT, DNSCrypt, ODoH
- RFC 8914 — Extended DNS Errors