Astro Incremental Builds: Configure cacheKey Without Serving Stale Pages
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:
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:
- Restore
node_modules/.astro/before the build. - Run the same Astro version and configuration used by the previous build.
- Build and inspect the output for a changed page and an unchanged page.
- 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:
pnpm exec astro build --forceThe 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.concurrencygreater than1disables the incremental cache and Astro renders every page.- Server-island pages are re-rendered unless their generated key is stable; the documented
ASTRO_KEYsetting controls that key. - Pages removed from
getStaticPaths()have their previous output cleaned up automatically. - Pages without a matching
cacheKeyare 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:
Report a typo or broken link, or suggest a related topic.