What your recursive DNS resolver adds
A recursive DNS resolver is the server your machines query directly. It owns no zones; it walks the tree to find an answer, caches it, and serves the cached copy until the TTL runs out.
In this chapter you point one at the authoritative server you built, using the unbound package already on the slice, and you prove the cache with a query run twice: the second reply comes straight from memory. Then you turn on validation, so the resolver not only remembers answers but refuses forged ones. This is the layer your laptop and your applications actually talk to, and it is what keeps a busy site fast without hammering the authoritative server behind it.
- A resolver caches each answer for its TTL, so a repeat query is served from memory with a query time of
0 msec. - The warm answer shows a lower TTL than the cold one, counting down in real time, which is the visible proof the record came from cache and not from a fresh lookup.
prefetch: yesrefreshes a popular record just before it expires, so callers almost never wait on the cold path.- A validating resolver sets the
adflag when the DNSSEC signatures on an answer check out, and drops data that fails.
Proving the cache: a cold query and a warm one
The clearest way to see a cache is to ask the same name twice and compare. The first lookup is cold: the resolver has nothing stored, so it fetches the record and starts a countdown from the record's TTL. The second is warm: it answers from memory, and the TTL is now lower because time has passed. Run the query, wait a few seconds, run it again.
dig @127.0.0.1 -p 5302 boxlab.space A

The cold answer shows the record at TTL 3600 with a query time of a millisecond or two. Repeat it after a short pause and the TTL reads 3595 with a query time of 0 msec. The falling number is the proof: the resolver is serving a stored record and ageing it, not asking the authoritative server again.
When the TTL reaches zero the next query is cold once more and the countdown restarts. That is caching doing its job, and it is why a resolver in front of your zone turns thousands of repeat lookups into near-zero work.
The resolver config: stub, forward and prefetch
A resolver's behaviour lives in a small config. Ours listens only on loopback, allows queries from localhost, points a stub zone at the authoritative server for boxlab.space so those answers come straight from it, and forwards everything else to an upstream. Read the file.
cat /etc/unbound/unbound.conf.d/dnslab.conf

Two lines earn their place. prefetch: yes tells unbound to refresh a popular record when its TTL is nearly spent, so the record stays warm and callers skip the cold path. The stub-zone block sends boxlab.space queries directly to your authoritative server on port 5301, which is how the resolver and the server you built connect into one system. The forward-zone sends everything outside that zone to an upstream resolver at 1.1.1.1, the pattern you use when you want a caching layer of your own in front of a public resolver.
Validation: the ad flag
A caching resolver should also validate. With DNSSEC turned on and the zone's trust anchor configured, unbound checks the signature on every answer and refuses data that fails. When validation succeeds it sets the authenticated-data flag. Ask for a signed record and request DNSSEC.
dig @127.0.0.1 -p 5302 boxlab.space A +dnssec

The header now reads flags: qr rd ra ad. The ra means the resolver did the recursion, and the ad means it validated the DNSSEC chain and the data is genuine. A resolver that did not validate would return the same address without ad, leaving you trusting whatever the network handed back. Validation is what turns a cache into a resolver you can rely on, and both the split views next chapter and the signing two chapters on build directly on it.
Run the query with and without +dnssec. The plain query answers from cache with ra; the DNSSEC one adds ad and a second record, the signature. If ad never appears, the resolver has no trust anchor for the zone yet, which the DNSSEC chapter sets up in full.
Going further: keep it a resolver, not an open one
A resolver on a small slice needs little tuning, but a few defaults matter. Bind it to loopback or an internal interface only, never straight to the public internet, or it becomes an open resolver that attackers borrow to amplify traffic at a victim. Size the cache to your traffic, leave prefetch on for a busy box, and keep validation enabled so a poisoned answer cannot slip through.
Point your applications, and if you choose the machine's own stub resolver, at this server, but keep the authoritative role on its separate port so the two never blur. A resolver that recurses for the world and an authoritative server that speaks for your zone are different jobs, and running them as different listeners is what keeps each one easy to reason about when something breaks.
Frequently asked questions
How do I know an answer came from the cache?
Query the name twice. The warm answer shows a lower TTL than the cold one and a query time near 0 msec, because the resolver served a stored record and aged its TTL rather than fetching it again. When the TTL hits zero the next query is cold and the countdown restarts from the record's full lifetime.
What does prefetch actually do?
With prefetch: yes, unbound refreshes a record whose TTL is nearly spent when a query arrives, then still serves the valid cached copy for that request. Popular records stay warm, so callers rarely wait on a cold lookup. It costs a few extra upstream queries in exchange for lower tail latency.
What is the difference between a forward zone and a stub zone?
A forward-zone hands queries to another recursive resolver that does the full walk for you, such as a public resolver at 1.1.1.1. A stub-zone points a specific zone straight at its authoritative servers, so your resolver talks to the source directly, which is how it reaches the zone you built on the same box.
Why should a resolver only listen on an internal address?
A resolver open to the internet can be abused to amplify traffic at a target, and it leaks the lookups your users make. Binding it to loopback or a private interface, and limiting access control to your own networks, keeps it a resolver for your machines instead of a tool handed to attackers.
What you can do now
You can run a validating caching resolver in front of your authoritative server, prove the cache with a cold and a warm query, read the config that stitches the stub zone and the upstream together, and confirm DNSSEC validation with the ad flag. Your two servers now act as one system: a resolver your machines talk to, backed by a zone you own.
So far every client gets the same answer for a name. The next chapter makes the same name answer differently depending on who asks: split-horizon DNS for internal names, so a private address stays inside and the public one goes out.