Most engineers treat DNS as a magic function: give it a name, get an IP. In reality, the name never gets "looked up" — it gets asked about through a chain of independent systems, each with its own cache, its own timeout, and its own failure modes. This is the whole path, step by step, from your process to the root servers.
Step 1: the resolver configuration
Before anything else, the kernel's resolver needs to know who to ask. On Linux that's
/etc/resolv.conf:
nameserver 127.0.0.53 # systemd-resolved stub, forwards upstream
nameserver 8.8.8.8 # fallback, used only if the first fails
options timeout:2 attempts:2 ndots:1Two details matter here. First, ndots:1 means a name with at least one dot (like
api.theruntimetheory.com) is treated as fully qualified and queried as-is; a bare
hostname like db is tried against the search domains first. Second, timeout:2 attempts:2 means each upstream gets 2 seconds, twice, before the resolver gives up and
returns an error to your application. A slow DNS server shows up in your request latency
as a hard, fixed number — not a gradual slowdown.
Step 2: caches are where most lookups end
The vast majority of DNS queries never leave the machine. glibc's nscd or
systemd-resolved keeps a local cache keyed by (name, type) with entries that expire
according to the record's TTL — the number of seconds the authoritative server
declared the answer valid. The cache is a performance layer, but it's also a consistency
layer: as long as the TTL hasn't expired, every process on the machine sees the same IP,
even if the DNS zone changed upstream five seconds ago.
Step 3: the recursive walk
If the local cache misses, the resolver performs a recursive resolution. Every resolver (starting from the stub on your machine, through your ISP's or Cloudflare's recursor) does the same walk:
client -> resolver: "what is api.theruntimetheory.com?"
resolver -> root server: "who serves .com?"
resolver -> .com TLD server: "who serves theruntimetheory.com?"
resolver -> theruntimetheory.com authoritative server:
"what is api.theruntimetheory.com?"
authoritative -> resolver: A 104.21.62.13 (TTL 300)
resolver -> client: A 104.21.62.13Each answer is a referral to the next zone, and the resolver caches every step of the walk. That's why a root server outage is survivable in practice: the root zone is cached for days, and only the TLD step needs refreshing.
Step 4: CNAME chains are extra lookups, not redirects
A CNAME record is an alias: it tells the resolver "the real name is X, keep going."
This is how every CDN works:
$ dig +short blog.theruntimetheory.com
blog.theruntimetheory.com.cdn.cloudflare.net. # CNAME
104.21.62.13 # A record, different zoneCritically, the client sees only the final A record — the extra lookup happens entirely inside the resolver, and each hop in the chain is cached separately with its own TTL. A CNAME pointing at a CDN with a 60-second TTL means your edge DNS is effectively uncached: every miss walks the chain again.
Step 5: EDNS and the TCP fallback
Almost all queries now carry EDNS(0), which negotiates a larger UDP payload (up to 4096 bytes) so answers fit in a single packet. But large answers still exceed UDP limits, and middleboxes still mangle big UDP packets. The protocol's escape hatch: the server responds with a truncated answer — the TC bit set — and the resolver is required to retry the query over TCP, where answers can be as big as needed.
$ dig +ignore +tcp example.com AAAA # force the TCP pathYou can trigger this yourself with a large TXT record: the first response arrives with
truncated: 1 and the resolver silently retries on TCP. When you see "timeout resolving"
errors, it's often this fallback failing — the UDP query worked, but the TCP retry hit a
firewall that blocks port 53/TCP.
Step 6: the protocol change — DoH and DoT
Classic DNS sends queries in clear UDP to port 53 — visible to your ISP, your Wi-Fi operator, and any middlebox. DoH (DNS over HTTPS) wraps the same query in HTTPS to port 443; DoT uses TLS on 853. The resolver path stays identical; only the last hop (client → resolver) becomes encrypted. The trade-off: DoH hides the query from the network, but hands it to a single resolver (your chosen DoH provider), which sees everything.
The mental model
resolv.confpicks the resolver; TTLs decide how often it's actually consulted.- CNAMEs are resolution-time indirection, invisible to the application.
- Truncation + TCP is the protocol's designed fallback, and the most common silent DNS failure.
- Every hop — stub, recursor, TLD, authoritative — is a separate cache with its own staleness window. "Propagation" is not magic; it's a countdown of TTLs.