06 Nov 2026 · linux, rede, dns · 4 min
what dns is and why the cache lies to you
DNS translates names to IPs, but the path has several cache and override layers. dig asks DNS only; getent goes through NSS, /etc/hosts and resolv.conf. Cache lies when the TTL has not expired yet.
DNS, in plain terms
DNS is a distributed database that answers which IP matches a name. The question is simple, the answer is not: between you and the authoritative server there are /etc/hosts, nsswitch.conf, resolv.conf, a local stub resolver and caches at several levels. Each can return a different answer or keep an old one. See resolv.conf(5) and nsswitch.conf(5).
The real resolution order
When a program calls getaddrinfo(), glibc follows /etc/nsswitch.conf. On most systems the line is:
hosts: files dns
Meaning: /etc/hosts first, then DNS. If /etc/hosts has an entry for the name, DNS is never consulted. That explains why dig example.com and getent hosts example.com can differ: dig ignores /etc/hosts and nsswitch.conf and asks the server in /etc/resolv.conf directly.
/etc/resolv.conf
Lists up to 3 nameservers, in order. The resolver tries the first; if it does not answer (timeout), it tries the second. Careful: if the first answers with NXDOMAIN, that counts as an answer and the second is not consulted. A broken internal server that denies your internal names will not be rescued by the secondary.
The file also defines search, which appends a domain to short names. A name with fewer than ndots (default 1) dots tries each suffix of the search list before asking the bare name. That generates unexpected queries and, in environments with a public suffix, man-in-the-middle risk.
How the cache lies
The DNS answer carries a TTL in seconds. Any cache on the path (stub resolver, recursive resolver, CDN) may keep the answer for that long. To know if you are reading cache, repeat the query and watch the TTL decrease: if it drops, it is cache; if it stays the same, it is a fresh answer from the authoritative.
# TTL in seconds in the answer
dig +noall +answer example.com @1.1.1.1
# example.com. 183 IN A 93.184.216.34
Changed DNS and it did not propagate? It is not propagation, it is cache expiring. Records do not travel outward; caches simply expire and ask again. Everything appears everywhere within one TTL from the last fetch by each cache.
The artifact
Script that prints resolv.conf, nsswitch.conf, tests getent on localhost and a nonexistent name, checks whether dig is available and simulates cache with TTL.
#!/usr/bin/env bash
set -u
echo "== 1. /etc/resolv.conf (what the resolver reads) =="
if [ -r /etc/resolv.conf ]; then
ls -l /etc/resolv.conf
cat /etc/resolv.conf
else
echo "file missing: glibc uses only 127.0.0.1 and the local domain"
fi
echo
echo "== 2. /etc/nsswitch.conf (the real order) =="
if [ -r /etc/nsswitch.conf ]; then
grep -E '^[[:space:]]*hosts:' /etc/nsswitch.conf || echo "no hosts line; glibc uses 'files dns'"
else
echo "file missing: glibc uses default 'files dns'"
fi
echo
echo "== 3. getent hosts (goes through NSS, like a real app) =="
for name in localhost example.invalid; do
printf '%-20s -> ' "$name"
getent hosts "$name" || echo "(no answer)"
done
echo
echo "== 4. dig (DNS only, ignores /etc/hosts and nsswitch) =="
if command -v dig >/dev/null 2>&1; then
dig +short example.com A | head -3
else
echo "dig not installed in this environment (apt install dnsutils)."
echo "on your system: dig example.com A"
fi
echo
echo "== 5. cache simulation with TTL =="
CACHE=/tmp/dns-cache.txt
echo "example.com 93.184.216.34 60" > "$CACHE"
now=$(date +%s)
expires=$((now + 60))
echo "stored: example.com -> 93.184.216.34, expires at $expires"
# first query: cache hit
if grep -q '^example.com' "$CACHE"; then
echo "query 1: cache HIT (did not go to DNS)"
fi
# second query after TTL 'expires'
sleep 1
now2=$(date +%s)
if [ "$now2" -ge "$expires" ]; then
echo "query 2: cache EXPIRED, goes to DNS again"
else
echo "query 2: still in TTL, cache HIT ($((expires - now2))s left)"
fi
echo "clearing cache"
rm -f "$CACHE"
echo "done."
How to verify
Run the script. The output should show the files dns order, localhost resolving via /etc/hosts and example.invalid with no answer. If dig is installed, it prints an IP for example.com. The simulation shows a cache hit and the TTL countdown.
If it breaks
- getent resolves but dig does not: the name is in /etc/hosts or in local cache. Use dig to see what DNS actually says.
- dig resolves but getent does not: /etc/hosts has a negative entry or nsswitch lacks
dns. Check the hosts line. - Changed DNS and nothing changed: cache with long TTL. Wait for the TTL or restart the local resolver (systemd-resolved, nscd). Do not edit resolv.conf by hand without checking if it is a symlink.
- resolv.conf reverted after reboot: NetworkManager or DHCP rewrote it. Configure via NetworkManager or netplan.
FAQ
dig and getent disagree, which is right? Depends on what you want to test. To know what an application will experience, use getent or resolvectl. To know what DNS contains, use dig.
What is TTL and who sets it? The authoritative server sets it, in seconds. Caches respect that value (or less, never more).
Why did the second nameserver not save me? Because the first answered, even if with NXDOMAIN. NXDOMAIN is an answer, not a failure.
Can I turn off the cache? You can lower the TTL, but not turn off the stub resolver cache without changing resolver config. In systemd-resolved, resolvectl flush-caches clears it.
search vs domains, what is the difference? search appends a suffix to short names; domain sets the local domain. Use search for multiple suffixes, domain for one.
Figure
Sources: resolv.conf(5), nsswitch.conf(5), dig(1). See also TLS handshake step by step and processes, ports and connections.