dig
dig asks a DNS server a direct question and prints exactly what it answers: the addresses a name resolves to, its mail and name servers, the TTLs, and the header status flags. Reach for it when you want DNS ground truth instead of whatever a browser or a cached lookup decides to show you, whether that is an early recon pass, verifying a homelab record, or answering “is that record actually pointing where I think it is?”
Mental model: dig [@resolver] name [type] [+options]. Omit the type and you get an A record; omit the resolver and it uses your system default.
New to DNS? Core concepts
- Record type: what you are asking for.
A(IPv4),AAAA(IPv6),MX(mail),NS(name servers),TXT(SPF/DMARC),SOA(zone info),CNAME(alias),PTR(reverse) - ANSWER section: the records that actually answer your query; each line reads name, TTL, class, type, value
- Resolver vs authoritative: a resolver (
@server) may reply from cache; the zone’s own NS is the source of truth, and theaaflag means the answer is authoritative - TTL: how many seconds a record may be cached before a fresh lookup; it is the number right before the type
- Recursion: you ask a resolver to chase the whole chain for you (
rd/raflags);+tracewalks it hop by hop yourself - status: the header outcome.
NOERROR(worked),NXDOMAIN(no such name),SERVFAIL(broken),REFUSED(declined) - Reverse lookup (PTR): an IP back to a name, the opposite direction from
A/AAAA
When to reach for dig (and when not)
Reach for it when you want DNS ground truth instead of a cached or browser-massaged answer: an early recon pass, confirming a record points where you think, reading SPF/DMARC TXT, finding a domain’s MX and NS, reverse-lookups, or tracing which hop breaks a lookup with +trace.
Reach for something else when you just need one quick line (host), DNS-over-TLS or DNS-over-HTTPS (kdig), or heavy DNSSEC debugging (drill). Once you have the records, whois adds registration and ownership context and nmap scans the addresses dig resolved to find live services.
Install, update, verify#
dig ships inside the DNS utilities package, not a package named “dig”.
sudo apt install -y dnsutils # Debian / Ubuntu / Kali
sudo dnf install -y bind-utils # Fedora / RHEL
brew install bind # macOS
sudo apt install --only-upgrade -y dnsutils # update
dig -v # version (note: -v, not --version)
command -v dig # confirm it is on PATH
On Windows, use the dig that ships with WSL or install ISC’s BIND tools. A one-off dig with no arguments queries the root servers (. NS) and is a quick “is DNS working at all?” smoke test.
Flags you’ll actually use#
Skim these first; the cheat sheet below is these flags combined.
| Flag | Does | Flag | Does |
|---|---|---|---|
@server | Query a specific resolver | +short | Values only, one per line |
-x IP | Reverse (PTR) lookup | +noall +answer | Answer section only |
-t TYPE | Set the type explicitly | +trace | Follow delegation from root |
-q NAME | Set the name explicitly | +norecurse | Ask cache only, no chasing |
-p PORT | Non-standard DNS port | +dnssec | Request RRSIG records |
-f FILE | Batch queries from a file | +cd | Checking disabled (skip DNSSEC) |
-4 / -6 | Force IPv4 / IPv6 transport | +tcp | Force TCP (AXFR, truncation) |
+multiline | Expand SOA / DNSKEY records | +time=N +tries=N | Timeout and retry budget |
-t and -q only matter when a name could be mistaken for a type; normally the positional order handles it.
Cheat sheet#
The first block trims verbosity one line at a time, so you can stop where the noise level is right. The rest swap one field (the type, the resolver, an option) per line.
# Trim the noise: stop at the line you need
dig DOMAIN # 1. full response: header, question, answer, stats
dig DOMAIN +noall +answer # 2. + just the answer section
dig +short DOMAIN # 3. + values only, one per line (A is the default type)
# Records (swap the type)
dig DOMAIN A # IPv4 address
dig DOMAIN AAAA # IPv6 address
dig DOMAIN MX # mail servers + priorities
dig DOMAIN NS # authoritative name servers
dig DOMAIN TXT # SPF / DMARC / verification strings
dig DOMAIN SOA # zone serial + primary NS
dig DOMAIN CNAME # alias target
dig _sip._tcp.DOMAIN SRV # service host + port (note the _svc._proto prefix)
dig DOMAIN CAA # which CAs may issue certs for the domain
dig DOMAIN DNSKEY # zone signing keys (signed zones)
# Concise output
dig +short DOMAIN MX # values only, any type
dig +noall +answer DOMAIN # answer section only, no header / stats
dig +multiline DOMAIN SOA # expand SOA / DNSKEY into labelled lines
dig +noall +answer DOMAIN A DOMAIN MX DOMAIN NS # several types, one run
# Pick the resolver
dig @RESOLVER DOMAIN A # ask a specific server (e.g. @1.1.1.1)
dig @RESOLVER DOMAIN A +short # compare against your system default
dig -p 5353 @RESOLVER DOMAIN A # non-standard DNS port
# Reverse
dig -x TARGET_IP +short # IP -> hostname (PTR)
# Debug and delegation
dig +trace DOMAIN # follow delegation root -> TLD -> authoritative
dig DOMAIN A +norecurse @RESOLVER # what this resolver has cached (no chase)
dig +dnssec DOMAIN A # show RRSIG records on signed zones
dig +cd DOMAIN A # checking disabled: fetch even if DNSSEC fails
dig +tcp DOMAIN A # force TCP instead of UDP
# Batch and tuning
dig -f names.txt +short # one query per line from a file
dig DOMAIN A +time=2 +tries=1 # fail fast: 2s timeout, no retries
# Zone transfer (lab / owned only)
dig @RESOLVER DOMAIN AXFR # full zone if misconfigured; usually REFUSED
Command breakdowns#
Each block below explains one situation, the exact command, and what to expect back.
Resolve a name to its IPv4 address#
dig DOMAIN A
An ANSWER SECTION with one or more A records. The number before the type is the TTL in seconds, and multiple A lines usually mean round-robin or a load balancer.
Get just the IP, nothing else#
dig +short DOMAIN
Bare addresses, one per line. A is the default type, so you can drop it. Ideal inside $(...) and shell loops where you only want the value.
Look up IPv6 addresses#
dig DOMAIN AAAA
AAAA records if present. An empty answer with NOERROR means the name simply has no IPv6, not a failure.
Find a domain’s mail servers#
dig DOMAIN MX +short
Each line is <priority> <host>; the lowest priority number is tried first. Feed those hostnames back into dig ... A to get the actual mail server IPs.
Read SPF, DMARC, and verification records#
dig DOMAIN TXT
dig _dmarc.DOMAIN TXT
SPF lives in the apex TXT (v=spf1 ...); DMARC sits under the _dmarc label. Long values arrive split across multiple quoted chunks, so concatenate them before parsing.
Find the authoritative name servers#
dig DOMAIN NS +short
The servers that own the zone. Query one of these with @ to get answers straight from the source instead of a cache.
Check the zone serial (SOA)#
dig DOMAIN SOA +noall +answer
The serial (often YYYYMMDDnn) tells you whether a change was actually published; the refresh, retry, expire, and minimum values follow it on the same line.
Query a specific resolver and compare answers#
dig @RESOLVER DOMAIN A +short
Points at one server instead of your default. Different answers between two resolvers means caching, split-horizon DNS, or geo-routing, not necessarily a mistake.
Check what a resolver has cached#
dig DOMAIN A +norecurse @RESOLVER
+norecurse sets rd=0, so a recursive resolver answers only from its cache instead of chasing the chain. An empty answer means it has not cached that record yet.
Reverse-lookup an IP to a hostname#
dig -x TARGET_IP +short
Returns the PTR record if one is configured. Many hosts have none, so an empty result here is normal, not an error.
Follow a CNAME chain to the real record#
dig DOMAIN A +noall +answer
If the name is an alias you will see the CNAME line, then the A it points to. Do not stop at the CNAME; the address is one hop further down.
Trace resolution from the root down#
dig +trace DOMAIN
Walks root -> TLD -> authoritative, printing each delegation, and bypasses your recursive resolver’s cache. Noisy, but it shows exactly which hop breaks a lookup.
Attempt a zone transfer (lab targets only)#
dig @RESOLVER DOMAIN AXFR
Dumps the entire zone if the server is misconfigured to allow it; a correct server answers REFUSED or Transfer failed. Only against infrastructure you own or are explicitly authorized to test.
Check DNSSEC signatures#
dig +dnssec DOMAIN A
Adds RRSIG records beside the answer on a signed zone. The ad flag in the header means the resolver validated them; use +cd to fetch the data even when validation would fail.
Query several records in one run#
dig +noall +answer DOMAIN A DOMAIN MX DOMAIN TXT DOMAIN NS
dig processes each name/type pair in sequence and prints one combined block, giving a fast full-record snapshot without four separate invocations.
Look up SRV or CAA records#
dig _sip._tcp.DOMAIN SRV
dig DOMAIN CAA
SRV maps a service to host plus port (note the _service._proto prefix); CAA lists which certificate authorities may issue certs for the domain.
Reading the output#
An ANSWER line reads left to right as name, TTL, class, type, value:
DOMAIN. 3600 IN A 198.51.100.10
The header’s status: is the outcome; read it as carefully as the answer:
| status | Meaning | Do next |
|---|---|---|
NOERROR + answer | Records exist; these are your facts | Read them |
NOERROR, empty | Name exists, but no record of that type | Query a different type (MX, TXT, NS) |
NXDOMAIN | Name does not exist from this resolver’s view | Check spelling; try another resolver |
SERVFAIL | Resolver could not complete the query | +trace, or query the authoritative NS |
REFUSED | Resolver declined | Normal on AXFR; otherwise you are off its allow-list |
Header flags: aa = authoritative (not a cache), ad = DNSSEC-validated, rd/ra = recursion desired / available, tc = truncated (retry with +tcp).
Troubleshooting#
| Problem | Cause | Fix |
|---|---|---|
command not found | dnsutils not installed / stale PATH | Install dnsutils (or bind-utils); open a fresh shell |
connection timed out; no servers could be reached | No resolver or no network | Check /etc/resolv.conf; try dig @RESOLVER |
status: NXDOMAIN | Name truly does not exist | Verify spelling; try another resolver |
status: SERVFAIL | Broken delegation or DNSSEC | Use +trace; query the authoritative NS directly |
status: REFUSED on AXFR | Server blocks zone transfer (normal) | Expected on secure servers; only test lab targets |
Empty answer, NOERROR | No record of that type | Query a different type (A, MX, TXT, NS) |
;; Truncated, retrying in TCP | UDP reply too large | Let dig retry, or force it with +tcp |
| Answers differ between resolvers | Caching, split DNS, or geo-routing | Compare default vs @RESOLVER; check TTL and the aa flag |
Gotchas#
- There is no package called “dig”. It lives in
dnsutils(Debian) orbind-utils(RHEL), so a search for a “dig” package finds nothing. NOERRORwith an empty answer is notNXDOMAIN. The name exists; it just has no record of the type you asked for. Query the right type before declaring DNS broken.+shorthides the status line. An empty result could be no record, the wrong name, or the wrong resolver. Re-run without it to seeNOERRORvsNXDOMAIN.- A cached answer is not authoritative. No
aaflag means it came from a cache, so query the zone’s NS (or@RESOLVER) for the source of truth. ANYis mostly dead. Resolvers now minimize or refuse it (RFC 8482), so query specific types instead of hoping one lookup returns everything.+traceignores your resolver. It starts at the root, so it will not reproduce a cache or split-horizon answer your applications actually see; use@RESOLVERfor that.
Pairs with#
whois adds registration and ownership context to the records dig returns, and nmap scans the addresses dig resolves to find live services. For a quick one-liner reach for host; for DNS-over-TLS or DNS-over-HTTPS use kdig; for heavy DNSSEC debugging drill is purpose-built. dig stays the universal, scriptable default for everything in between.