DNSresolverrecursivecacheTTLroot

Following One Lookup Through Every Cache

A name is resolved by falling through caches — browser, OS, recursive resolver — and, on a full miss, by the resolver walking root → TLD → authoritative, with every hop’s answer stored for its TTL; which cache you hit explains both the speed and why two people get different answers.

ConceptualLinux

The problem

You type engineer-atlas.dev for the first time on a new laptop. Four caches sit between the browser and the truth, all of them empty. How many round trips does that first lookup cost, where does every subsequent lookup stop, and why does your colleague across the desk get a different IP?

Progressive depth

The same mechanism at different altitudes — start where you are.

Name in, address out, with caches in between

The application asks the OS; the OS asks a resolver; the resolver either knows the answer from a previous question or goes and finds it by walking down from the root. Every answer comes with an expiry time so it can be reused.

The chain of caches

Resolution is a fall-through. The browser has its own small host cache. Below it the OS stub resolver checks /etc/hosts and, on many systems, a local caching daemon (systemd-resolved on most Linux desktops, mDNSResponder on macOS, the DNS Client service on Windows). Below that, the recursive resolver — configured by DHCP or by hand — has a large shared cache. Only when all of those miss does anyone contact an authoritative server.

Each cache is independent, has its own view of the TTL, and is flushed by different means. That independence is a feature for performance and the source of every "works on my machine" DNS story. Note also that the stub does not usually cache negative answers or partial results; the recursive resolver does — the resolver’s cache is the one that matters.

Where a lookup for engineer-atlas.dev can stop
  1. Browser host cacheshort-lived (on the order of a minute in Chromium); flushed by chrome://net-internals or a restart
  2. OS stub resolver/etc/hosts first; then a local cache (systemd-resolved, mDNSResponder); then resolv.conf
  3. Recursive resolverISP, corporate, 1.1.1.1, 8.8.8.8 — a large shared cache; this is where TTL really lives
  4. Root serversreferral to the .dev servers; cached for days (NS TTL 2 days)
  5. TLD servers (.dev)referral to engineer-atlas.dev’s NS; cached for the delegation TTL
  6. Authoritative serverthe answer: A 203.0.113.10, TTL 300

A cold resolution, step by step

dig +trace makes the recursive resolver’s job visible by doing it from your machine: ask a root server, follow the referral, ask the TLD, follow the referral, ask the authoritative server. Three round trips (plus one to fetch the root hints) is the cold cost; with a typical root or TLD instance 10–30 ms away, that is 50–100 ms before a TCP connection can even start. A warm resolver answers the same question from cache in well under a millisecond of its own time, so the round trip to the resolver dominates.

Notice what each server returns. The root does not know engineer-atlas.dev; it returns the NS records for dev. and, in the additional section, the addresses of those servers (glue) so the resolver can reach them. The dev. servers do the same for engineer-atlas.dev.. Only the last server answers the actual question. In practice, a resolver that has served any .dev name in the last two days already has the first referral cached and starts at step two.

dig +trace, abbreviated (times and addresses illustrative)
$ dig +trace www.engineer-atlas.dev A

.                        518400  IN  NS  a.root-servers.net.
.                        518400  IN  NS  b.root-servers.net.
;; Received 239 bytes from 127.0.0.53#53 in 0 ms            # root hints from the local resolver

dev.                     172800  IN  NS  ns-tld1.charlestonroadregistry.com.
dev.                     172800  IN  NS  ns-tld2.charlestonroadregistry.com.
;; Received 743 bytes from 198.41.0.4#53 (a.root-servers.net) in 12 ms

engineer-atlas.dev.      10800   IN  NS  ns1.example-dns.net.
engineer-atlas.dev.      10800   IN  NS  ns2.example-dns.net.
;; Received 188 bytes from 216.239.32.105#53 (ns-tld1...) in 18 ms

www.engineer-atlas.dev.  300     IN  A   203.0.113.10
;; Received 67 bytes from 198.51.100.53#53 (ns1.example-dns.net) in 24 ms

Caching, TTL and negative caching

Every record carries a TTL in seconds set by the zone owner. A cache may serve the record for that long and must then discard it. The TTL counts down as the record sits in a cache, and a resolver hands its remaining TTL to the client, which is why dig shows 287 for a record whose zone says 300. A TTL is a promise the zone owner makes to the world: "you may be this stale". Choosing it is a trade between the load and latency of frequent re-fetching and the time a change takes to be seen everywhere — DNS Record Types and What They Are For discusses the migration playbook.

Negative caching (RFC 2308) is the part people forget. "This name does not exist" (NXDOMAIN) is also cached, for a duration taken from the zone’s SOA record (its *minimum* field, conventionally 5–60 minutes for public zones). If you query a record before you created it, the resolver you used will keep saying it does not exist until that negative TTL expires — a classic self-inflicted outage during a deployment. Errors like SERVFAIL are also briefly cached by many resolvers.

Resolvers do not honour TTLs perfectly. Most cap very long TTLs (a day or a week is a common ceiling), some enforce a minimum, browsers keep their own short cache regardless of TTL, and some resolvers serve stale data when the authoritative servers are unreachable (RFC 8767 "serve-stale") — which keeps sites up during authoritative outages and also keeps them pointing at dead addresses. Do not reason about DNS as if every cache read the specification.

  • TTL is set by the zone, counted down by caches, and returned as the remaining value.
  • NXDOMAIN is cached too, for the SOA minimum; do not query a name before it exists.
  • Resolvers clamp, cap and sometimes serve stale; the TTL is a ceiling on staleness in theory and a suggestion in practice.

Why two people get different answers

Conceptual

The same name legitimately resolves to different addresses for different clients, and understanding why is most of DNS debugging. Cache state: your resolver cached the old address five minutes ago; your colleague’s resolver fetched the new one just now. Resolver choice: you use the corporate resolver, which has a split-horizon view of internal names; the browser uses DoH to a public resolver, which does not. Geographic answers: CDNs and large services hand out an address near the *resolver* that asked, on the assumption that clients are near their resolver — which is why using a resolver in another country makes a CDN slow.

EDNS Client Subnet (ECS) refines that last case: a public resolver like 8.8.8.8 can include the client’s network prefix (typically a /24) in its query to the authoritative server, which then answers for the client’s location rather than the resolver’s, and caches per subnet. It fixes the geography problem and multiplies the number of distinct answers a resolver may hold for one name. Anycast resolvers (Internet Routing: Autonomous Systems and BGP) add another layer: 1.1.1.1 is hundreds of independent caches, and which one you reach depends on BGP.

The consequence for you: dig @8.8.8.8 name and dig @1.1.1.1 name and dig name from the affected machine are three different questions, and during an incident you should ask all three. DNS Debugging: Who Answered, and With What? gives the procedure.

Glue and the bootstrap problem

Two circularities make DNS work at all. First: to ask the root you must know its address before you have DNS — every resolver ships with a root hints file of the thirteen names and their addresses, and refreshes it by asking one of them (the *priming* query). Second: if engineer-atlas.dev’s name servers are ns1.engineer-atlas.dev, resolving the name server’s name requires the zone you are trying to reach. The parent breaks the loop by storing the name servers’ addresses alongside the delegation — the glue records you saw in the +trace additional sections.

Stale glue is a real failure: you renumber your name servers, update your own zone, and forget that the registry still holds the old addresses. Resolvers that follow the delegation reach the old servers, which either no longer answer or answer with old data. The fix is at the registrar, not in your zone file. It is the DNS equivalent of updating the record but not the pointer to it.

Key points

  • Resolution falls through browser → OS → recursive resolver caches; only a full miss reaches authoritative servers.
  • A cold lookup is root → TLD → authoritative, three round trips; a warm one is one round trip to the resolver.
  • Each hop’s answer is cached for its TTL; the resolver hands down the remaining TTL.
  • NXDOMAIN is cached (negative caching, from the SOA minimum) — querying a name before it exists poisons your own resolver for minutes.
  • Different answers for different people are normal: cache state, resolver choice, geo answers, ECS, split horizon, anycast resolvers.
  • Glue records and root hints break the two bootstrapping loops; stale glue at the registrar is a silent outage.

Why does this exist?

Mechanisms are answers to constraints. Open each question before reading the answer.

Why cache at all if it causes staleness?

Without caches every page load would cost three round trips to servers that would then receive the entire internet’s query load. TTLs are the zone owner’s dial between freshness and cost; caching is the only reason DNS scales.

Why cache "does not exist"?

Typos, probes and misconfigured clients generate an enormous number of queries for names that do not exist; without negative caching every one of them would hit the authoritative servers. The price is that a name created seconds after it was first asked for is invisible for minutes.

Why does the recursive resolver do the work rather than the client?

Sharing: one resolver serving ten thousand clients answers almost everything from cache. Simplicity: the stub in every operating system, phone and IoT device stays a few hundred lines. Policy: an organisation can filter, log, or split-horizon at one point.

Resolve engineer-atlas.dev

Resolve engineer-atlas.dev
Three caches before a single packet leaves the machine; three servers after. Every answer carries a TTL that decides how long each cache may keep it.
  1. Browser cache
  2. OS cache (getaddrinfo)
  3. Recursive resolver 203.0.113.53
  4. Root server (.)
  5. TLD server (.dev)
  6. Authoritative ns1.engineer-atlas.dev
missThe browser keeps a tiny cache of recent answers. Miss: ask the OS.
$ dig +trace engineer-atlas.dev  (simulated)
; browser cache: engineer-atlas.dev → miss
Real resolution varies: which caches exist (browser, OS, a router, the ISP's resolver, a public resolver), how the resolver handles expired records, and CDN authoritatives that answer differently by geography. The shape is fixed; the timings are not.
1/7 · cold
Simulated

How it fails

What the failure looks like from inside real software.

  • A record changed with a 1-hour TTL: half the users reach the new server, half the old for the next hour; the on-call engineer, whose resolver refreshed first, cannot reproduce.
  • Querying new-service.example.com from the deploy script before the record was created: the resolver caches NXDOMAIN for the SOA minimum and the service is "not found" for 30 minutes after the record exists.
  • A resolver clamping the 30-second TTL a failover system relies on to 300 seconds: failover "takes five minutes" for users of that ISP and thirty seconds for everyone else.
  • Split-horizon plus browser DoH: the intranet works in curl and is NXDOMAIN in Chrome on the same laptop.
  • Users on a foreign resolver (a VPN, or 8.8.8.8 without ECS support at the authoritative side) getting a CDN edge on another continent: high latency for exactly one group of clients.
  • Authoritative servers down while resolvers serve stale: the site "works" for a while for some people, then disappears cache by cache over hours instead of all at once.

Follow it through every layer

This lesson is one node of a longer journey. Zoom out, then zoom back in.