skip to content

You deploy a new build of a Next.js App Router app. Which of Next.js's caching layers survive that deployment and which are thrown away?

level: seniorimportance: should knowfreq 42%

answer

  1. output is a build product
  2. input is not
  3. one layer deliberately outlives releases
  4. fresh container, fresh filesystem
  5. open tabs keep their copies

basics

~20 s

The persistent server data layer is designed to survive a deployment, so cached responses come back after the new build ships. Stored route output is a build product and is replaced. Per-render memoization is irrelevant, and the browser's Router Cache lives in each open tab until it reloads.

solid answer

~50 s

The one that surprises people is that the **Data Cache survives** — it is meant to persist across deployments, so a new build does not by itself hand you fresh upstream data. The **Full Route Cache does not**: stored rendered output is a product of the build, and a new build replaces it, which is why a prerendered page picks up code changes immediately while its data may not. **Request memoization** never spans requests, so a deploy is meaningless to it. The **Router Cache** lives in each user's browser, so a tab that was open before the deploy keeps whatever segments it had until the user reloads. The self-hosting caveat matters in practice: the persistent layer is backed by storage on disk, so a container that ships a fresh filesystem every deploy starts empty unless you persist that directory or point `cacheHandler` in `next.config` at a shared store.

go deeper

for a junior

Know that shipping a new build does not automatically mean every cached value is refreshed, so 'I deployed, why is it still old' is a real and expected question.

for a middle

Explain the asymmetry: rendered output belongs to the build and is replaced, while cached upstream responses are independent of the build and are kept.

for a senior

Bring the operational half — releases that change data shape need explicit invalidation, and filesystem-backed caching does not survive container replacement or spread across replicas without a shared handler.

for a principal

Own the release contract: which changes require cache invalidation as part of shipping, what the upstream stampede budget is when you invalidate broadly, and how version skew between old and new clients is handled after every deploy.

## The short answer, layer by layer | Layer | After a deployment | |---|---| | Request Memoization | Meaningless — it never lived longer than one render | | Data Cache | **Survives** by design; entries are still served | | Full Route Cache | Replaced; rendered output is a product of the build | | Router Cache | Lives in the user's browser; an open tab keeps its segments until reload | The interesting part of this question is not the table, it is why the two server layers behave differently and what that asymmetry does to a release. ## Why stored route output goes and stored data stays The Full Route Cache holds output — the rendered result of routes Next.js could render statically. That output is generated from your code, so it is inseparable from the build that produced it. Ship a new build and the old output is no longer valid, because the components that produced it have changed. Discarding it is the only correct behaviour. The Data Cache holds input — responses from upstream systems. Those responses have nothing to do with which build of your app asked for them. From the framework's point of view, throwing them away on every deploy would mean a stampede of upstream traffic every time you fix a typo in a footer, which is a genuinely bad default for a busy site. So the persistent data layer is designed to outlive deployments, and entries stay valid until something revalidates them. ## The release-day consequence This is where teams get hurt, and it is what an interviewer is listening for. You change how you transform an upstream response — you start requesting an extra field, or you fix a parsing bug — and you deploy. Rendered output is regenerated with the new code, so you would expect the fix to be live. But the new code is fed from stored responses captured by the *old* code, and those responses do not contain the new field. The page renders new markup around old data, and the symptom looks like your deploy silently did not happen. Two honest mitigations exist. The first is to treat the change as a data-shape change and make the release invalidate the affected entries as part of shipping, rather than hoping the deploy did it. The second is to change what the entries key on when the shape of what you store changes, so the old entries can no longer be found by the new code. Which one you pick is a judgement call about blast radius: invalidating everything at release time is simple and costs an upstream stampede, while re-keying is surgical but requires that you actually know which reads changed. ## The self-hosting caveat "Persists across deployments" is a statement about the framework's intent, not a guarantee your infrastructure honours. The default backing store for cached data is the filesystem, under the build's cache directory. That has direct consequences: - **Container deploys ship a fresh filesystem.** A new image with no persisted volume starts with an empty cache regardless of what the docs say about persistence. Everything is a miss on the first request after a deploy. - **Multiple instances do not share it.** Each replica has its own copy, so hit rates fall with replica count and an invalidation applied on one instance is not visible to the others. The fix for both is the same shape: give the layer a shared, durable home. Next.js exposes `cacheHandler` in `next.config` for exactly this, so a self-hosted deployment can point the cache at a shared store rather than at per-container disk. Being able to say that out loud is what separates "I read the caching page" from "I have run this in production." ## The client side of a deploy The Router Cache is not on your servers at all, so a deploy does not reach into it. A user whose tab has been open across the release still holds the segments they had already visited, and those segments were produced by the previous build. That memory is discarded whenever the page fully reloads. The practical implication is one every long-lived single-page app shares: the set of users on the new build and the set on the old build overlap for a while after every release. That is a version-skew problem to design for at the API and asset level, not something the caching layers solve for you. ## How to answer it crisply A strong answer is three sentences: rendered output is a build product and is replaced; stored upstream data is deliberately independent of the build and survives; and the client's copy lives in browsers you do not control, so it clears on reload. Then add the caveat that survival of the data layer depends on the storage actually surviving, which in a container deployment it usually does not unless you arranged it.

  • Why is a data layer that survives deploys sometimes exactly what you do not want?
    Because a fix to how you shape or parse an upstream response does not invalidate anything. The new code runs against responses captured by the old code, so the deploy appears to have done nothing. Either invalidate the affected entries as part of the release, or change what those entries key on so the new code cannot find the old ones.
  • You self-host in containers and the cache looks empty after every deploy. Why?
    Because the default backing store is the filesystem inside the image, and a new container ships a fresh one. Persist that cache directory across deploys, or point `cacheHandler` in `next.config` at a shared external store — which you need anyway once you run more than one replica, since otherwise each instance keeps its own copy.
  • Does a deployment reach into users' browsers to clear their cached segments?
    No. The Router Cache is in browser memory on machines you do not control, so a tab open across the release keeps segments produced by the old build until it reloads. That overlap of old and new clients after every release is a version-skew problem to design for, not something the caching layers resolve.

saying these in an interview costs you the question

  • Says a deploy clears every Next.js cache
  • Thinks fresh rendered output implies fresh data
  • Assumes filesystem-backed cache survives container replacement
  • Expects replicas to share one cache automatically

context