You ping the server. It answers. You open the app in a browser. Connection refused — or worse, it hangs for thirty seconds and then times out. The server is up, the cable is plugged in, yet nothing resolves. Nine times out of ten, the problem is not the network at all. It is DNS.
I have spent more than a few late nights on this exact symptom. Here is the field-tested path I now follow every time, so you do not have to reinvent it at 2 a.m.
Start with the cheapest test: nslookup / dig
Before touching configs, confirm whether name resolution itself works:
dig +short example.com nslookup example.com - Empty output or
SERVFAIL→ the resolver is the problem, not the remote host. - A correct IP but the app still fails → resolution is fine; look at the app or firewall next.
-
NXDOMAIN→ the name genuinely does not exist (typo, or the record was deleted).
On Linux, systemd-resolved often listens on 127.0.0.53. If you point tools at the wrong resolver you get confusing results, so check /etc/resolv.conf first.
The usual suspects
-
/etc/resolv.confgot overwritten. Cloud-init, DHCP leases, and VPN clients all rewrite this file. Ifnameserverpoints at a dead IP, every lookup fails while pings to raw IPs still work. - A flaky internal resolver. Pointing everything at one corporate DNS server that intermittently drops queries produces the classic "works, then doesn't" pattern.
-
systemd-resolvedstub confusion.127.0.0.53is the stub. Query it directly withdig @127.0.0.53 example.comto isolate whether the stub or the upstream is at fault. -
nscd/dnsmasqcache poisoning or staleness. A cachedNXDOMAINcan linger. Flush withsudo systemd-resolve --flush-caches(or restartdnsmasq). - Firewall blocking UDP 53. Outbound DNS is UDP/53 (and TCP/53 for large responses). A strict egress rule that allows TCP but not UDP will let
digsometimes work and the app sometimes fail.
A 5-minute triage checklist
# 1. Does raw resolution work? dig +short example.com # 2. Which resolver are we using? cat /etc/resolv.conf # 3. Is the stub resolving, or just the upstream? dig @127.0.0.53 example.com dig @8.8.8.8 example.com # 4. Is UDP 53 actually open outbound? nc -vzu 8.8.8.8 53 # 5. Clear any poison cache sudo systemd-resolve --flush-caches If step 2 shows a dead nameserver, fix that first — it explains everything.
The fix that actually sticks
For servers, I stopped relying on DHCP-assigned resolvers. I pin a known-good pair in /etc/systemd/resolved.conf:
[Resolve] DNS=8.8.8.8 1.1.1.1 FallbackDNS=9.9.9.9 Domains=~. Then sudo systemctl restart systemd-resolved. The Domains=~. line is the part people miss: it tells the resolver to use these servers for every domain, not just ones matching a search list.
Why this matters beyond the outage
DNS failures are silent and asymmetric. The database host resolves, the cache host does not, and suddenly one service throws timeouts while dashboards stay green. Baking a deterministic resolver config into the base image — and alerting on dig latency, not just on ping — has saved me more 2 a.m. pages than any other single change.
If you want the full postmortem write-up with packet captures and the exact tcpdump filter I use to catch dropped UDP/53 queries, it lives on my ops notes site: ops.jiunotes.top/en. The same notes are mirrored on the main site: jiunotes.top/en.