>_ josetilabs.com
← blog

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.

bash
# 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.

bash
#!/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

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

Diagram showing the resolution order: application calls getaddrinfo, NSS reads nsswitch.conf, files queries /etc/hosts, dns queries /etc/resolv.conf, cache returns or forwards, authoritative server answers
Real order of a resolution: each layer can answer or pass along. dig skips the first three.

Sources: resolv.conf(5), nsswitch.conf(5), dig(1). See also TLS handshake step by step and processes, ports and connections.

Read next

// By e-mail

Posts published here also go out by e-mail. Delivery has not started yet: whoever leaves an address now joins the first batch.

I store only your e-mail, the date and the record of this consent. No cookies, no trackers, nothing shared with third parties. Details in the privacy policy.