Back to blog

Broken by Design  ·  Layer 7 · How Networks Fail · October 2026

Broken by Design
The Resolver That
Needed Itself

“The internet is slow” — but the link is idle and pings are instant. Every action pauses for a few seconds, then works. Or the whole site is dead after a reboot and recovers only when you touch the DNS server by hand. DNS is a hidden hard dependency for almost everything, and when its resolution path loops back on itself, the symptom is never “DNS is down” — it is “everything is slow.”

Broken by Design DNS Resolver Loop Dependencies resolv.conf Timeouts

Almost nothing on a network talks to an IP directly — it asks DNS for a name first. That makes the resolver a hidden hard dependency: when it is slow, everything that resolves a name is slow, and when it loops, the loop does not fail fast — it waits out a timeout, so the whole network feels like treacle rather than an outage.

The nastiest version is circular: resolver A forwards to B, B forwards back to A, and the query ping-pongs until it dies. Or a service needs DNS to start, but the DNS server needs the very thing that service provides — so after a power cut nothing comes up until you break the cycle by hand. Or, most mundane of all, resolv.conf lists a dead server first, and every lookup burns a five-second timeout before falling back to the one that works.

Part of

01

The Query That Never Lands

Resolver A forwards to B; B forwards back to A. The query circles until it times out — and every name lookup on the network waits with it.

Clientneeds a name Resolver Aforwards to B Resolver Bforwards to A loop: A → B → A → … → timeout The query never reaches an authoritative answer; after a few seconds it fails, and the client retries. To the user this is “the network is slow,” never “DNS is looping.”

The reason it reads as slowness and not an outage is the timeout. A looked-up name eventually fails, the app retries another resolver or the cache serves a stale hit, and things limp along — every action gated by a multi-second pause. Users report “slow internet”; the link graph is flat and idle.

02

The Shapes It Takes

Three different loops, all of which stall rather than fail cleanly.

ShapeHow the loop forms
Forwarder loopResolver A forwards to B and B forwards to A (often a split-horizon or a copy-pasted config). The query never escapes.
Bootstrap dependencyA service needs DNS to start; the DNS server needs that service (or the network it brings up). After a reboot, neither can start first.
Dead first in resolv.confThe first nameserver is down; every lookup waits the full timeout before trying the second. Five seconds, every name.
Self-referential recordA name resolves to a host that is only reachable by first resolving that name — a dependency knot inside one zone.

03

The Symptoms

Slow, not down; works after a pause; worst from cold.

What you seeWhat it tells you
Everything pauses ~5s then worksA dead first resolver, or a loop that times out and falls back.
Link idle, pings instant, yet “slow”The delay is in name resolution, not bandwidth or latency.
Whole site dead after a reboot, fine once pokedA bootstrap dependency — the cycle only bites from cold.
SERVFAIL in the resolver logA query that never reached an answer — frequently a loop.

04

The Fix: Break the Cycle, Fail Fast

Point forwarders outward, give the resolver no dependency on what it resolves, and order resolv.conf so the first entry is always reachable.

A forwarder must point out of the loop — to a public resolver or the roots — never back at a peer that points to it. Internal names that infrastructure needs to boot should be served locally (a local zone, a hosts entry) so they resolve even when the wider network is still coming up. And the first server in resolv.conf must be one that answers, so a lookup never starts with a timeout.

Break the loop (RouterOS forwarder + a local record)
# forward OUT to public resolvers, not back to a peer that forwards here
/ip dns set servers=9.9.9.9,1.1.1.1 allow-remote-requests=yes
# serve infra names locally so they resolve during boot, no upstream needed
/ip dns static add name=nas.lan address=10.20.10.20
/ip dns static add name=controller.lan address=10.20.10.25
⚠ Gotcha — the five-second tax of a dead first nameserver

A host with nameserver 10.0.0.9 (dead) listed before nameserver 1.1.1.1 (fine) does not fail — it waits the resolver timeout on every lookup before falling back. The cumulative effect across a page full of names is seconds of stall that looks exactly like a slow link. Remove dead entries; do not just add a good one after them. On systemd-resolved and friends, check the effective order, not just the file.

05

Catch It in 30 Seconds Field check

Time a single lookup. If name resolution is slow while the link is idle, you have found it.

Measure resolution, see the loop
# how long does ONE name take? and which server actually answers?
dig +stats example.com @10.20.10.1     # look at “Query time” and SERVER
dig +trace example.com                 # watch where it stalls in the chain
# is the first resolv.conf entry even up?
for ns in $(grep ^nameserver /etc/resolv.conf | awk '{print $2}'); do \
  echo -n "$ns "; dig +time=1 +tries=1 example.com @$ns +short | head -1; done

A multi-second query time on an idle link, a dig +trace that stops progressing, or a first nameserver that does not answer — any of these confirms it. Break the forwarder loop and fix the resolver order, and the “slow internet” evaporates.

Takeaways

  1. DNS is a hidden hard dependency — when its path loops, the symptom is “slow,” never “DNS down.”
  2. Loops time out, they do not fail fast — that is why it reads as a bandwidth problem on an idle link.
  3. Forwarders must point out of the loop — to public resolvers or the roots, never back at a peer.
  4. Do not let the resolver depend on what it resolves — serve infra names locally so the network can boot.
  5. A dead first resolv.conf entry taxes every lookup — remove it, do not just append a good one.
  6. Time one lookup to confirm — slow resolution on an idle link is the whole diagnosis.

“Everything is slow” but the link is idle?

A looping or dead-first resolver turns every name lookup into a multi-second stall that looks like a bandwidth problem. I map the resolution path, break the loop, and remove the hidden dependencies that make a network fragile on boot.

Book a Discovery Call →