Skip to content

3. The architecture

Three kinds of server, one protocol between them, and almost every DNS misunderstanding is a confusion about which one you are talking to.

┌──────────────┐
│ APPLICATION │ getaddrinfo("x.com")
└──────┬───────┘
│
┌──────▼───────┐
│ STUB RESOLVER│ in libc, or systemd-
│ │ resolved, or the browser
│ knows: one
│ server's IP │
└──────┬───────┘
│ "resolve this for me" (RD=1)
┌──────▼─────────────────┐
│ RECURSIVE RESOLVER │
│ caches everything │
│ walks the tree │
└──┬────────┬────────┬───┘
│ │ │ "what do you know?" (RD=0)
┌──▼──┐ ┌──▼───┐ ┌──▼────────┐
│ROOT │ │ .com │ │example.com│
│ . │ │ │ │ │
└─────┘ └──────┘ └───────────┘
AUTHORITATIVE SERVERS
each answers only about its own zone

It is deliberately stupid. It sends one question to a configured address and trusts the reply.

Terminal window
cat /etc/resolv.conf
resolvectl status
resolvectl query example.com
getent hosts example.com
nscd -g 2>/dev/null | head
/etc/resolv.conf
nameserver 10.0.0.1
nameserver 1.1.1.1
search example.com internal.example.com
options timeout:2 attempts:2 rotate
ndots:1 edns0 single-request
nameserver up to 3 are used. they are
tried IN ORDER, not load-
balanced, unless `rotate`.
search appended to names with
fewer than `ndots` dots
ndots default 1
timeout default 5 SECONDS. the
single worst default here.
KUBERNETES SETS ndots:5
SO `dig api.example.com` INSIDE A POD
TRIES, IN ORDER:
api.example.com.default.svc.cluster.local
api.example.com.svc.cluster.local
api.example.com.cluster.local
api.example.com.ec2.internal
api.example.com. ← finally
FIVE WASTED ROUND TRIPS, EACH AN
NXDOMAIN, FOR EVERY EXTERNAL LOOKUP.
Terminal window
cat /etc/resolv.conf # inside a pod

The fix is either a trailing dot on external names (api.example.com.), a per-pod dnsConfig with ndots: 2, or NodeLocal DNSCache. It is the single most common DNS performance problem in Kubernetes, and the symptom is tail latency rather than failure.

This is where the work happens: it starts at the root, follows referrals, caches every answer, and returns one final result.

WHAT IT MAINTAINS
a cache, keyed on (name, type, class),
expiring by TTL
a NEGATIVE cache for NXDOMAIN/NODATA
a list of root server addresses (the
"root hints" — or it just asks a
root, and gets the real list)
infrastructure data: which
nameservers are fast, which are
timing out
DNSSEC validation state, if enabled
RESOLVER SOFTWARE
Unbound the standard choice for a
validating recursive
resolver
BIND 9 does everything; the
reference implementation
Knot Resolver modern, modular, fast
PowerDNS
Recursor widely deployed at ISPs
dnsmasq a forwarder with a cache,
not a real recursor
systemd-
resolved a stub plus a cache; a
forwarder, not a recursor
CoreDNS plugin-based; Kubernetes'
default

The distinction between a recursor and a forwarder matters. A recursor walks the tree itself. A forwarder passes the question to another resolver and caches the reply. dnsmasq and systemd-resolved are forwarders; configuring them as if they were recursors produces a system that works only as long as the upstream does.

Terminal window
unbound-control stats_noreset | grep -E 'total.num|cache'
dig +trace example.com # watch a recursion happen
dig +norecurse @1.1.1.1 example.com # is it already cached there?

dig +norecurse against a public resolver is a neat trick: if you get an answer with a TTL lower than the zone’s configured value, it was cached, and the remaining TTL tells you how long ago.

It holds one or more zones and answers only about them. It never performs recursion, and it never caches anything, because it already has the truth.

WHAT AN ANSWER LOOKS LIKE
the `aa` flag set (Authoritative
Answer)
the data, from its own zone file
OR a REFERRAL, if the question is
below a delegation
OR NXDOMAIN, if the name does not
exist in its zone
OR REFUSED, if the zone is not one it
serves
AUTHORITATIVE SOFTWARE
NSD minimal, fast,
authoritative-only
Knot DNS modern, fast, DNSSEC-
native
BIND 9 the reference; does both
roles
PowerDNS
Authoritative database backends
CoreDNS can be authoritative too
Route 53 / NS1 / Cloudflare / Google
Cloud DNS managed
Terminal window
dig @ns1.example.com example.com SOA # expect aa
dig @ns1.example.com other.com SOA # expect REFUSED
named-checkzone example.com /etc/bind/db.example.com
kdig +tls @1.1.1.1 example.com
PRIMARY ("master")
holds the editable zone. the SOA's
serial number is incremented here.
SECONDARY ("slave")
pulls the zone by AXFR/IXFR and serves
identical answers. authoritative, but
not editable.
BOTH ARE LISTED IN THE NS RECORDS, AND
RESOLVERS DO NOT KNOW OR CARE WHICH IS
WHICH.

That last line is the point of the design: a zone’s NS set is a list of equally-good answer sources, and the replication mechanism between them is invisible to the rest of the world. Chapter 8 covers the transfer protocols.

The root servers are not thirteen machines

Section titled “The root servers are not thirteen machines”
THERE ARE 13 ROOT SERVER NAMES
a.root-servers.net … m.root-servers.net
BECAUSE 13 NS RECORDS PLUS GLUE WAS THE
MOST THAT FIT IN A 512-BYTE UDP PACKET
IN 1993.
THERE ARE OVER 1,900 ACTUAL INSTANCES,
run by 12 independent organisations,
in hundreds of locations.
THEY SHARE ADDRESSES VIA ANYCAST.
Terminal window
dig . NS +short
dig +nsid @k.root-servers.net . SOA # which instance answered?
curl -s https://root-servers.org/ | head -1
THE SAME IP ADDRESS ANNOUNCED FROM MANY
PLACES. BGP ROUTES EACH CLIENT TO THE
NEAREST ONE.
you in Berlin ──┐
├─▶ 193.0.14.129 (Frankfurt
you in Tokyo ───┤ instance)
└─▶ 193.0.14.129 (Tokyo
instance)
SAME ADDRESS. DIFFERENT MACHINE.
NO DNS TRICKERY INVOLVED — this is
routing, not resolution.

Anycast is how every serious DNS operator achieves both latency and DDoS resilience: an attack hits whichever instance is nearest the attacker, and the rest of the world is unaffected. It also explains why dig from two locations can show different NSID values for the same server name, and why “the root server is down” is almost never a meaningful statement.

A recursive resolver sees every name every client looks up, before any connection is made, which makes it the cheapest control point in a network — and the most contested one.

WHAT RESOLVERS ARE USED FOR BEYOND
RESOLVING
BLOCKING malware and phishing
domains (Quad9, corporate
filters, Pi-hole)
FILTERING content policy, safe search
LOGGING the single best signal for
detecting compromise and
exfiltration
SPLIT-HORIZON internal names that do not
exist publicly
STEERING rewriting answers to point
at a local cache or proxy
Terminal window
dig @9.9.9.9 +short malware-test.example # Quad9 blocks
dig @1.1.1.3 +short adult-test.example # family resolver
pihole -q doubleclick.net

The mechanism is the same in every case: the resolver returns NXDOMAIN, or an address it chose, instead of the real answer. That is also, precisely, what a cache-poisoning attack does — which is why DNSSEC validation and resolver-based filtering are in permanent tension, and why filtering resolvers must strip the AD flag or decline to validate.

JUST CACHE FOR ONE HOST
systemd-resolved — already installed
dnsmasq — tiny, also does DHCP
A REAL RECURSIVE RESOLVER FOR A NETWORK
Unbound — the default recommendation
Knot Resolver — modular, scriptable
BIND 9 — if you need its features
AUTHORITATIVE, SELF-HOSTED
NSD or Knot DNS — fast, minimal,
authoritative-only on purpose
PowerDNS Authoritative — database
backends, APIs
BIND 9 — views, dynamic update
IN A CLUSTER
CoreDNS — plugin chain, Kubernetes
native

The split between authoritative-only and recursive-capable software is deliberate and worth respecting. NSD and Knot DNS cannot recurse at all, which means they cannot be turned into an open resolver by a configuration mistake — a meaningful security property, and the reason the root servers run software of exactly that kind.

STUB MISCONFIGURED
→ one host cannot resolve anything.
/etc/resolv.conf, search domains,
ndots.
RECURSIVE RESOLVER DOWN
→ everyone using it is offline, and
nothing is wrong with your domain.
RECURSIVE RESOLVER CACHE POISONED/STALE
→ some users get wrong answers. the
hardest class to diagnose, because
it is invisible from the authority.
AUTHORITATIVE SERVERS DOWN
→ your domain disappears for everyone
whose cache expires. gradual, then
total.
DELEGATION BROKEN AT THE REGISTRAR
→ total, immediate for new lookups,
and you cannot fix it from your own
nameservers.

The asymmetry is worth internalising: problems on the resolver side affect some users and are invisible to you, while problems on the authoritative side affect all users and are visible to everyone. Which direction a report comes from narrows the cause enormously.

  • Three roles: a stub that trusts one configured server, a recursive resolver that walks the tree and caches, and an authoritative server that holds a zone and never recurses or caches.
  • /etc/resolv.conf’s default timeout:5 attempts:2 means a dead first nameserver costs ten seconds. This is the root of most “DNS is slow” investigations.
  • ndots:5 in Kubernetes makes every external lookup try five cluster-local suffixes first. Fix it with a trailing dot, a per-pod dnsConfig, or NodeLocal DNSCache.
  • A recursor walks the tree; a forwarder asks someone else and caches. dnsmasq and systemd-resolved are forwarders, and treating them as recursors builds a dependency you did not intend.
  • An authoritative answer carries the aa flag. Its four possible outcomes are data, a referral, NXDOMAIN, or REFUSED for a zone it does not serve.
  • Primary and secondary are an implementation detail invisible to resolvers — the NS set is just a list of equally authoritative sources.
  • There are 13 root server names because that is what fit in a 512-byte packet in 1993, and over 1,900 instances behind them via anycast.
  • Anycast is routing, not resolution: the same IP announced from many places, with BGP choosing. It works for DNS precisely because queries are single-packet, stateless and idempotent.
  • The failure asymmetry is diagnostic: resolver-side problems affect some users and are invisible to you; authority-side problems affect everyone and are visible to all.

Next: records — the types that matter, what each is actually for, and the ones that are routinely misused.

Written by Sepuri Sai Krishna

© 2026CC BY 4.0Source