> ## Content Index
> Fetch the complete content index at: https://debugly.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# Every Layer That Caches a DNS Answer Between You and the Record
- URL: https://debugly.dev/how-dns-caching-layers-stack/
- Published: 2026-09-08T11:30:00.000Z
- Updated: 2026-10-17T05:58:08.000Z
- Description: A DNS answer passes through four or five caches before it reaches your code, each with its own TTL and its own opinion.
- Author: Rohit Bhadani
- Tags: Deep Dive, Networking, Tooling

You changed an A record and half the world saw it in a minute and the other half took an hour. Nobody is lying to you. DNS is not one cache. It is a stack of caches, and the time you experience is the maximum of whichever layers your query passed through.

Understanding the stack turns "DNS is slow to update" from folklore into a list of places to check, in order.

This was observed across Chrome 133, Ubuntu 24.04 resolvers and a recursive resolver on Linux 6.8\. The layering is the same everywhere.

## The stack, top to bottom

**1\. The application.** Browsers cache DNS for their own lifetime, with their own internal TTL that is often a few minutes and not directly tied to the record's TTL. Your HTTP client library may also cache, and JVMs famously cache with their own security properties. This layer is the one that makes "it works in a fresh incognito window" true.

**2\. The OS stub resolver.** Your machine's resolver, `systemd-resolved` on many Linux boxes, `mDNSResponder` on macOS, the DNS client service on Windows. It caches answers for the record's TTL, but may clamp or override it.

**3\. The recursive resolver.** Your ISP's or your configured resolver, like 1.1.1.1 or 8.8.8.8\. This cache is shared by everyone using that resolver, which is why a change propagates to some populations before others. It honours the TTL but many resolvers clamp a maximum and enforce a minimum.

**4\. Intermediate caches you forgot.** A corporate proxy, a container's embedded resolver, a service mesh sidecar, a CDN's own resolver. Each adds a hop with its own cache.

**5\. The authoritative servers.** Not a cache, but they can serve stale or different answers during a misconfiguration, and zone transfers between primaries and secondaries add lag of their own.

A query stops at the first layer that has a fresh answer. So the latency you observe on a change is dominated by whichever layer was holding the old answer the longest.

## TTL is a promise, not a law

The record's TTL tells caches how long they may hold it. Several realities complicate it.

Resolvers often enforce a floor and a ceiling. A TTL of one second may be raised to thirty. A TTL of a week may be capped at a day. So the number you set is a suggestion that each layer edits.

Negative answers are cached too, with their own TTL, the SOA minimum. If someone queried your domain while it did not exist, or while a record was absent, that absence is cached, and adding the record later does not invalidate the cached absence. This is why "it resolves for me but not for the customer" can persist after you fix the record, and it is the negative caching piece of [the domain resolved everywhere except where I was sitting](https://debugly.dev/cname-resolved-cert-pending-stale-resolver/).

And a resolver that is about to expire an entry may serve it stale while it refetches, if configured to, trading freshness for latency.

## Watching a change propagate

The tool is `dig`, and the key is to query each layer directly and compare.

```bash
# authoritative answer, bypassing every cache
dig @ns1.yourdomain.com yourdomain.com A +short

# what the public recursive sees
dig @8.8.8.8 yourdomain.com A +short

# what your local resolver currently serves
dig yourdomain.com A +short

```

Each of those is a different layer. The difference between them, plus the `ANSWER` section's TTL countdown, tells you exactly where the old answer lives.

To see the remaining TTL on a cached answer, query twice a few seconds apart and watch the TTL number count down. A value that does not match what you set means a layer clamped it.

On the local machine, you can also flush and retest to isolate the OS layer:

```bash
sudo systemd-resolve --flush-caches   # systemd-resolved
sudo resolvectl flush-caches

```

If flushing locally fixes it instantly, the stale layer was your own machine, which is the cheapest possible outcome.

## The browser, the stubborn one

The browser layer is the one that frustrates people most because it is the least observable. Chrome keeps its own cache and its own internal TTL. There is a net internals DNS view, but the honest workflow is to test in a fresh profile or a different browser to confirm the browser was the holder.

If you are debugging a user's complaint, remember their browser and their OS and their resolver are all independent caches. "Works on my machine" for DNS is literally expected during propagation.

## Designing so the stack does not hurt you

A few habits make the stack a non issue most of the time.

Lower the TTL well before a planned change, a day or more ahead, so that by the time you switch, every layer's holding period is short. Changing the record first and the TTL after is backwards and buys you nothing.

Keep TTLs modest for records you expect to change, and long for records that are stable. There is no prize for a five minute TTL on a record that never moves, and it just shifts load and variance.

Do not rely on DNS for failover speed. If you need sub minute failover, DNS TTLs are the wrong mechanism, because the worst case is always some resolver somewhere holding the old answer. Use a load balancer or health checked routing instead and let DNS point at the stable thing.

## The mental model

DNS propagation is not a single wave moving across the internet. It is the independent expiry of many caches that happened to be populated at different times. The record changed instantly at the authoritative servers. Everything downstream changes when its own clock runs out.

So when someone asks how long propagation takes, the honest answer is: as long as the longest lived cache that holds the old answer, which you can find by walking the stack with dig until the answers agree.

For the Kubernetes flavour of the same layering, where the cluster adds its own resolver and its own cache, see [Kubernetes DNS debugging](https://debugly.dev/kubernetes-dns-debugging-nslookup/). And for the case where the latency came from the resolver rather than a stale answer, see [the latency spike that was DNS](https://debugly.dev/latency-spike-that-was-dns/).

---

Published on [debugly.dev](https://debugly.dev/) by [Rohit Bhadani](https://debugly.dev/about/) | Founder of [Krova Cloud](https://krova.cloud/?ref=debugly.dev)