Career OS

DNS: Finding The Server

This site already has a DNS Deep Dive in the Web Infrastructure track — that’s the owner’s view: registrars, zone files, managing records, why “propagation” feels slow. This module is the other half: the runtime view — what actually happens in the milliseconds when your code resolves a name, and exactly what to do when it fails. Same system, opposite end of the wire.

The Goal

By the end of this module you can:

  • Trace a name lookup through every cache and server it touches — and say which step answered, and why the second lookup is near-instant
  • Read nslookup output line by line instead of pattern-matching for an IP
  • Distinguish NXDOMAIN, SERVFAIL, and a DNS timeout — three different errors pointing at three different culprits
  • Explain the JVM’s own DNS cache and the classic failover incident it causes
  • Override DNS locally with the hosts file — and run your own API on your own fake domain in two minutes
  • Recognize UnknownHostException and curl error 6 as DNS problems before wasting time elsewhere

The Lesson

The resolution chain — who answers what

Your code calls restTemplate.getForObject("https://api.example.com/..."). Java doesn’t speak DNS itself — it asks the OS, and the OS works through a chain, stopping at the first level that knows the answer:

flowchart LR
    A[Your code] --> B[OS stub resolver]
    B --> C[Hosts file then OS cache]
    C --> D[Recursive resolver]
    D --> E[Root server]
    D --> F[TLD server]
    D --> G[Authoritative server]
StepWhoWhat it knows
Stub resolverYour OS (Windows DNS Client service)Nothing — it just asks the configured resolver and caches replies
Hosts file + OS cacheYour machineManual overrides, plus answers from the last few minutes
Recursive resolverYour ISP, or 8.8.8.8 / 1.1.1.1Does the actual legwork; caches aggressively for everyone using it
Root server13 logical servers worldwideOne thing only: where the TLD servers are (“for .com, ask over there”)
TLD serverRuns .com, .in, etc.Where each domain’s authoritative servers are
Authoritative serverWhoever hosts the domain’s zoneThe actual records — the source of truth

The cold-path lookup (resolver → root → TLD → authoritative) happens rarely. Almost every real lookup is answered from a cache in step 2 or 3 — which is the next point.

TTL — one number, two faces

Every DNS answer carries a TTL: how many seconds anyone may cache it. The Deep Dive covered why this makes record changes slow from the owner’s side. From the runtime side, flip it around: TTL is why DNS is fast. Without caching, every HTTP request on earth would queue up at thirteen root servers. With it, your hundredth call to the same API resolves in microseconds from local memory.

Same number, two faces: high TTL = fast lookups, slow changes. Low TTL = agile changes, more lookup traffic. As the engineer consuming DNS, the face you care about is: when something changes upstream, every cache between you and the authoritative server keeps lying to you until its TTL runs out — including one cache most developers don’t know exists, inside your own JVM.

Record types you’ll actually query

The full record table (MX, NS, SOA, the A-vs-CNAME apex rule) lives in the Deep Dive — don’t relearn it here. From code, you touch a shorter list:

RecordYou hit it whenNote
A / AAAAEvery connection to a hostnameA = IPv4, AAAA = IPv6. The answer your socket actually needs
CNAMEResolving anything hosted on a platformAn alias hop: www.github.comgithub.com → A record. Resolvers chase the chain for you
SRVOne-liner: maps a service name to a host and port — how Kubernetes and Consul do service discovery
TXTOne-liner: free-form text — domain verification, SPF; you’ll query it while debugging email or ownership checks

What the JVM adds — the hidden cache

Here’s the layer that bites Java developers specifically: InetAddress keeps its own cache of successful lookups, on top of the OS cache. Default: 30 seconds for successful lookups, 10 seconds for failures — but older JVMs and some setups cached successful lookups forever (until restart).

The classic incident, and it’s a fintech favourite: the database fails over. Ops updates the DNS record to point at the new primary within seconds. Every other system recovers. Your Java service keeps connecting to the old IP — the dead one — because the JVM cached the lookup and never asks again. Someone restarts the app “to see if it helps,” it helps, and nobody learns anything. Now you know: the fix is the JVM setting, not the restart.

The knob lives in java.security (under your JDK’s conf/security folder):

networkaddress.cache.ttl=30
networkaddress.cache.negative.ttl=10

Interview-grade sentence: “DNS answers are cached at the browser, OS, resolver — and inside the JVM, and the JVM one is the one that survives a record change and causes failover incidents.”

Failure modes — three errors, three culprits

DNS fails in exactly three flavours, and they are not the same error:

ErrorWho said itWhat it meansYour move
NXDOMAINThe authoritative server, definitively”This name does not exist.” Typo, wrong domain, record genuinely missingCheck the spelling, check the environment (is it api.staging. vs api.?)
SERVFAILThe recursive resolver”I tried and failed to get an answer.” Broken zone config, DNSSEC validation failure, unreachable authoritative serversNot your typo — the domain’s DNS is sick. Try another resolver to confirm
TimeoutNobodyThe resolver itself never answered. Network down, firewall blocking port 53, wrong resolver configuredThis isn’t about the domain at all — your path to DNS is broken

Note the rhyme with TCP’s failure table from module 02: a definitive negative answer (NXDOMAIN, like refused) means the system works and is telling you a truth; silence (timeout) means infrastructure is broken.

In Java, all three usually surface as one exception: java.net.UnknownHostException. In curl, error 6: Could not resolve host. When you see either, stop reading application logs — the request never left the building. Drop to nslookup to find which of the three flavours you’re in.

The hosts file — the override that beats everything

Before any cache, before any resolver, Windows checks one plain text file:

C:\Windows\System32\drivers\etc\hosts

A line like 127.0.0.1 splitease.local means: for this machine, that name resolves to that IP, full stop. No TTL, no resolver, no network. It beats every cache because it’s checked first.

Three things to know about it:

  • Legitimate use: test a real domain against a new server before changing public DNS, or give local services memorable names (you’ll do this in Build This).
  • Why malware loves it: one line redirecting a bank’s domain to an attacker’s IP hijacks every program on the machine — which is why editing it needs admin rights and why antivirus watches it.
  • The debugging trap: a forgotten hosts entry from last year’s testing produces “DNS lies” that no amount of ipconfig /flushdns clears. When one machine resolves a name differently from every other machine, check the hosts file first.

Split-horizon, in two lines

Some networks answer the same DNS question differently depending on who’s asking: on the office VPN, internal-api.company.com resolves to a private 10.x address; off VPN, it doesn’t resolve at all. So “works on VPN, UnknownHostException off it” isn’t flaky DNS — it’s DNS doing its job for two different audiences.

See It Move

Step through a full cold-cache resolution below (press ▶ Play):

🧑 You🔁 Resolver🌐 Root🏷️ .com📇 Authoritative

1. You type ayrisglobal.com. Your browser asks its Resolver: “what’s the IP?” Nothing cached — so the hunt begins.

🧑 You🔁 Resolver🌐 Root🏷️ .com📇 Authoritative

2. The resolver asks a Root server. Root doesn’t know the IP, but points the way: “ask the .com servers.”

🧑 You🔁 Resolver🌐 Root🏷️ .com📇 Authoritative

3. The resolver asks the .com TLD server. It says: “ask ayrisglobal’s own nameserver.”

🧑 You🔁 Resolver🌐 Root🏷️ .com📇 Authoritative

4. The resolver asks the Authoritative nameserver — the one that actually knows: “ayrisglobal.com = 104.21.45.12.”

🧑 You🔁 Resolver·✅ 104.21.45.12

5. The resolver hands the IP back to your browser and caches it (so next time is instant). Only now can the browser open a connection.

Step through it and notice:

  • The root and TLD servers never hand out the final IP — each just points one level down the hierarchy. Only the authoritative server knows the answer.
  • The recursive resolver does all the legwork and then caches the result — for everyone who shares that resolver, not just you.
  • The answer travels back with a TTL attached — that number decides everything about the warm-cache run.
  • Warm cache: two steps and done. This is the path nearly every request in production actually takes.

Check The Concept

How This Shows Up At Work

  • The failover that didn’t. Postmortem line you’ll actually read someday: “DNS was updated in 40 seconds; the Java services required restarts.” Now you’re the person in the room who says networkaddress.cache.ttl and turns a recurring incident into a one-line config fix.
  • “Works on VPN, broken at home.” A teammate’s UnknownHostException for an internal API, reproducible only off-VPN. That’s split-horizon DNS, not a flaky app — five minutes instead of a half-day of confusion.
  • The lying laptop. One developer’s machine resolves the staging domain to an IP nobody recognizes. Every cache was flushed, the mystery persists — until someone checks the hosts file and finds a test entry from months ago. You’ll check it first.
  • Interview round. “What happens when you type a URL?” — the DNS chapter of that answer is this module: hosts file, OS cache, recursive resolver, root, TLD, authoritative, TTL on the way back, plus the JVM cache if they probe Java specifics. Most candidates say “DNS resolves the domain” in four words and stall.
  • Reading the right error. A payment webhook integration fails. Junior reads HTTP logs; you read Could not resolve host, realize no packet ever left, run nslookup against two resolvers, and report “their domain’s DNS is returning SERVFAIL — it’s on their side” with evidence.

Build This

You’ll dissect real lookups, then give your own API its own domain without spending a rupee on a registrar. Reminder from module 02: type curl.exe, not curl, in PowerShell.

  1. Resolve a name and read every line:
nslookup example.com
Server:  dns.google
Address:  8.8.8.8

Non-authoritative answer:
Name:    example.com
Addresses:  2606:2800:21f:cb07:6820:80da:af6b:8b2c
          93.184.215.14

Line by line: Server / Address is which resolver answered you (yours will show your ISP’s or router’s — mine here is Google’s). Non-authoritative answer means it came from the resolver’s cache, not straight from the authoritative server — you’re seeing the warm path from the visualizer. Then the name and its addresses: one AAAA (IPv6) and one A (IPv4). Your numbers may differ — that’s DNS being a distributed system, not a bug.

  1. Ask a specific resolver instead of your default:
nslookup example.com 8.8.8.8

Same shape of answer, but now you chose the resolver. This is your standard move when you suspect your default resolver is lying or sick: ask 8.8.8.8 and 1.1.1.1, compare. Two resolvers agreeing means the answer is real; disagreeing means caches mid-change or split-horizon.

  1. Follow a CNAME chain:
nslookup -type=CNAME www.github.com
Non-authoritative answer:
www.github.com  canonical name = github.com

www.github.com has no address of its own — it’s an alias pointing at github.com, which holds the A record. Resolvers chase this automatically; you just made the hop visible.

  1. Run your API on your own domain. Open Notepad as Administrator (it’s a protected file), then open:
notepad C:\Windows\System32\drivers\etc\hosts

Add this line at the bottom and save:

127.0.0.1  splitease.local

With splitease-api running, call it by name:

curl.exe http://splitease.local:8080/actuator/health
{"status":"UP"}

Sit with that for a second: no registrar, no nameserver, no money — and splitease.local resolves, because the hosts file outranks the entire global DNS hierarchy on your machine. (Try nslookup splitease.local too — it fails, because nslookup queries DNS servers directly and skips the hosts file. Ping and curl use the real OS chain; nslookup doesn’t. Remember that asymmetry.)

  1. Now remove the line, save, and run the curl again. Stale hosts entries are exactly the debugging trap from the lesson — don’t leave one behind. If the name still resolves, ipconfig /flushdns clears the OS cache.

  2. Break it — NXDOMAIN. Ask for a name that cannot exist:

nslookup this-domain-does-not-exist-9381.com
*** dns.google can't find this-domain-does-not-exist-9381.com: Non-existent domain

“Non-existent domain” is Windows-speak for NXDOMAIN: a definitive, authoritative “no such name.” Notice it came back fast — the system worked perfectly and delivered a negative answer.

  1. Break it — the error your code will show. Now curl the same name:
curl.exe http://this-domain-does-not-exist-9381.com
curl: (6) Could not resolve host: this-domain-does-not-exist-9381.com

Error 6 is curl’s surface for any resolution failure — it flattens NXDOMAIN, SERVFAIL, and timeout into one message, exactly like Java flattens them into UnknownHostException. That’s why step 6 matters: the application error tells you it’s DNS; only nslookup tells you which DNS problem.

Where to Practice

ResourceWhat to do thereHow long
howdns.worksRead the full comic start to finish — the resolution chain as a story; then redraw the chain from memory20 min
Cloudflare Learning Center (cloudflare.com/learning)Read “What is DNS?” and “What is a DNS server?” — map each server type to a step you saw in the visualizer25 min
curl docs (curl.se)Look up the --resolve flag — curl’s built-in way to fake DNS for one command, no hosts file needed; try it against your API10 min

Check Yourself

  1. Walk the cold-cache chain: who does your stub resolver ask, and who does the legwork from there?
  2. What does “Non-authoritative answer” in nslookup output actually tell you?
  3. NXDOMAIN vs SERVFAIL vs timeout — one sentence each on who is speaking and what’s broken.
  4. Your Java app keeps hitting a dead IP after a DNS failover. Name the cache responsible and the setting that controls it.
  5. Why does nslookup splitease.local fail while curl splitease.local:8080 succeeds, given your hosts file entry?
  6. Why does the same TTL value make DNS fast and make changes slow?
  7. A name resolves to a private 10.x address on the office VPN and fails to resolve at home. What is this called and is it a bug?
  8. UnknownHostException appears in your logs. What’s the first command you run, and what are you trying to distinguish?
Answers
  1. The stub resolver checks the hosts file and OS cache, then asks the configured recursive resolver (ISP, 8.8.8.8, 1.1.1.1). On a cache miss, the recursive resolver does the legwork: root server → TLD server → authoritative server, then caches the answer and returns it.

  2. The answer came from the resolver’s cache, not directly from the domain’s authoritative server. It’s almost always fine — it just means you’re on the warm path, and the answer could be up to one TTL stale.

  3. NXDOMAIN: the authoritative side definitively says the name doesn’t exist — check spelling/environment. SERVFAIL: the recursive resolver tried and failed to get an answer — the domain’s DNS setup is sick. Timeout: the resolver itself never replied — your path to DNS (network, port 53) is broken.

  4. The JVM’s own InetAddress cache. Controlled by networkaddress.cache.ttl (and networkaddress.cache.negative.ttl for failures) in the JDK’s java.security file — 30 seconds by default today, effectively forever in older setups.

  5. nslookup sends queries directly to a DNS server, bypassing the OS resolution chain entirely — so it never sees the hosts file. curl resolves through the OS, where the hosts file is checked before everything else.

  6. Caches may serve the answer for TTL seconds without re-asking — that’s what makes lookups near-instant. The same permission means a changed record keeps being served stale from every cache until each one’s TTL expires.

  7. Split-horizon DNS. Not a bug — the network deliberately gives different answers to internal and external askers, so internal names simply don’t exist outside.

  8. nslookup <the-hostname> (then again against 8.8.8.8). You’re distinguishing NXDOMAIN (name is wrong/missing) from SERVFAIL (their DNS is broken) from timeout (your resolver path is broken) — three different culprits hiding behind one Java exception.

Explain it out loud: Narrate what happens in the first 50 milliseconds after your code calls an API by hostname for the very first time — every cache checked, every server asked, what travels back, and where the answer gets stored. Then narrate the second call. If the two stories sound the same, revisit TTL and the warm path.

Still Unclear?

I ran nslookup against my default resolver and against 8.8.8.8 and got
different IPs for the same domain. List the legitimate reasons this happens
(CDNs, caches mid-TTL, split-horizon) and how I'd tell which one it is.
Then quiz me with three made-up nslookup outputs and ask me to diagnose each.
Explain the JVM DNS cache incident properly: a Spring Boot app, HikariCP,
a PostgreSQL failover where DNS now points at a new IP. Walk through what
each layer (OS cache, JVM InetAddress cache, the pool's existing TCP
connections) does, and why BOTH the DNS cache and the pool matter here.
Here is the hosts file syntax I used and the curl output I got: [paste].
It is not resolving. Walk me through the checklist: admin rights, file
encoding, ipconfig /flushdns, and how to prove which resolution step is
failing — without editing anything for me.

Why AI Can’t Do This For You

DNS failures are environmental: the answer depends on which machine asked, through which resolver, with which caches warm, behind which VPN. AI sees none of that. It can describe NXDOMAIN beautifully, but it can’t run nslookup from your box and your production pod and compare — and that comparison is the diagnosis. The engineer who personally watched the same name resolve differently through two resolvers debugs in minutes what others escalate in hours.

There’s also a trust layer: hosts-file hijacks, split-horizon surprises, and stale JVM caches all produce systems that lie convincingly. The only defense is knowing the resolution order cold — what’s checked first, what can override what — so when a machine swears a domain points somewhere impossible, you know exactly which of the five layers is lying. You built that order by hand today, including faking your own domain and watching nslookup ignore it.

Module done? Add it to today’s tracker

Saves your progress on this device.