802 words
4 minutes

Astro Incremental Builds: Configure cacheKey Without Serving Stale Pages

2026-08-20
Frontend
Astro
/
Frontend
/
Development
/
Troubleshooting

Astro 7.2’s experimental incrementalBuild flag can reuse the output of unchanged prerendered pages, but it is not a general “build only changed files” switch. A page generated by getStaticPaths() needs a cacheKey that changes whenever its data changes, and the previous build cache must be available. If either condition is missing, Astro renders the page again; if the key is too stable, the output can be stale.

Enable the flag only for cacheable static routes#

Start with the smallest configuration change:

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
incrementalBuild: true,
},
});

The feature applies to static pages returned from getStaticPaths(). Give each entry a key that represents the data used to render that page:

export async function getStaticPaths() {
const posts = await fetchPosts();
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
cacheKey: `${post.slug}:${post.updatedAt}`,
}));
}

The exact key is application-specific. A content digest, source revision, or updated timestamp works only if it changes whenever the rendered result should change. A key based only on the slug tells Astro that a post is unchanged even when its title, body, or related data has changed.

For content collections, Astro documents the loader-provided digest as a convenient key:

const entries = await getCollection('docs');
return entries.map((entry) => ({
params: { slug: entry.id },
props: { entry },
cacheKey: String(entry.digest),
}));

Persist the cache between builds#

Astro stores the incremental cache in cacheDir, which defaults to node_modules/.astro/. The output directory is cleared at the start of a build, then skipped pages are restored from that cache. A clean CI runner that does not restore the directory will still produce a correct build, but it will render every page again.

Restore the project’s .astro cache before astro build, and save it after a successful build. Scope the CI cache to the project and its dependency/configuration identity; reusing a cache from an unrelated checkout makes the result harder to explain. The cache is an optimization, not a replacement for the source files or lockfile.

The practical sequence is:

  1. Restore node_modules/.astro/ before the build.
  2. Run the same Astro version and configuration used by the previous build.
  3. Build and inspect the output for a changed page and an unchanged page.
  4. Save the cache only after the build passes.

Know what invalidates a page#

Astro hashes the page’s module dependency graph, including layouts, components, and imported files. A code change in one of those dependencies can invalidate the affected pages even when their cacheKey is unchanged. A change to Astro configuration or project dependencies invalidates the entire cache.

Middleware needs special care: the current documentation notes that middleware changes do not invalidate cached pages. If middleware can change prerendered HTML, force a full rebuild after changing it:

Terminal window
pnpm exec astro build --force

The same command is useful when you suspect the cache itself rather than the key. Do not delete all of node_modules to test a rendering cache; --force gives you a narrower comparison.

Check the limitations before enabling it in CI#

The feature is experimental and has explicit boundaries:

  • build.concurrency greater than 1 disables the incremental cache and Astro renders every page.
  • Server-island pages are re-rendered unless their generated key is stable; the documented ASTRO_KEY setting controls that key.
  • Pages removed from getStaticPaths() have their previous output cleaned up automatically.
  • Pages without a matching cacheKey are rendered on every build.

For a large content site, measure a full build and an incremental build with the same cache state. The goal is not to make the build appear faster in one local run; it is to confirm that the saved cache is restored in the deployment environment and that changed content invalidates the pages it should.

The safe mental model is a content-addressed shortcut: cacheKey describes the page data, Astro hashes the code path, and the persisted cache supplies the previous HTML. If any input is outside those boundaries, use astro build --force or include that input in the key before trusting the result.

The existing Astro content collection storage guide covers a different large-site boundary: splitting the generated data store when one file is too large. Storage size and incremental rendering are separate decisions.

FAQ#

Q: Does incrementalBuild work for every Astro page?#

A: No. It targets static pages generated through getStaticPaths() with a matching cacheKey. Pages without that key are rendered again, so adding the flag alone does not make every route incremental.

Q: Why does Astro rebuild everything in CI?#

A: The incremental cache is stored in cacheDir, defaulting to node_modules/.astro/. If CI does not restore that directory before the build, Astro has no previous output to reuse. Also check that build.concurrency is not greater than 1.

Q: How do I force Astro to ignore the incremental cache?#

A: Run pnpm exec astro build --force. Use it after a middleware change or when debugging a suspected cache-key problem, then restore the normal build only after verifying the page data keys.

References:

Astro: Experimental incremental static builds

Astro 7.2 release notes

Astro issue #17613: Incremental static build limitations

Astro Incremental Builds: Configure cacheKey Without Serving Stale Pages
https://laplusda.com/en/posts/astro-incremental-static-builds-cachekey/
Author
Zero
Published at
2026-08-20
License
CC BY-NC-SA 4.0
Was this article useful?

Report a typo or broken link, or suggest a related topic.