“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.”
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.
01
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.
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
Three different loops, all of which stall rather than fail cleanly.
| Shape | How the loop forms |
|---|---|
| Forwarder loop | Resolver A forwards to B and B forwards to A (often a split-horizon or a copy-pasted config). The query never escapes. |
| Bootstrap dependency | A 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.conf | The first nameserver is down; every lookup waits the full timeout before trying the second. Five seconds, every name. |
| Self-referential record | A name resolves to a host that is only reachable by first resolving that name — a dependency knot inside one zone. |
03
Slow, not down; works after a pause; worst from cold.
| What you see | What it tells you |
|---|---|
| Everything pauses ~5s then works | A 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 poked | A bootstrap dependency — the cycle only bites from cold. |
| SERVFAIL in the resolver log | A query that never reached an answer — frequently a loop. |
04
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.
# 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
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
Time a single lookup. If name resolution is slow while the link is idle, you have found it.
# 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
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 →