Upgrading to Astro 7: the Rust compiler is fast, the cold cache is what bit me

Astro 7 moved the compiler and the Markdown pipeline to Rust and switched the bundler to Rolldown. The upgrade was quick; the failures it surfaced afterwards were not.

Astro 7 shipped on 22 June 2026, and the pitch is one sentence long: this release is all about speed.

Quite a lot changed underneath:

  • The .astro compiler was rewritten in Rust
  • Markdown and MDX now run through a new Rust-powered pipeline
  • The rendering engine was replaced with a queue-based approach
  • Vite 8, and with it the Rolldown bundler
  • 15–61% faster builds in Astro’s own benchmarks
  • Route caching stabilised, plus experimental CDN cache providers for Netlify, Vercel and Cloudflare
  • Advanced Routing: a src/fetch.ts entrypoint that can take over the request pipeline
  • Agent detection, a background dev server, and structured JSON logs for AI coding assistants

This site is 533 pages, 40 stylesheets and a 532-page search index. The upgrade itself was cheap. What cost me time was everything that surfaced after it.

Trap one: a CJS-only dependency stops being pre-bundled

The first astro sync after upgrading died with:

require is not defined

The cause was not my code. The content collection (src/content.config.ts, via the glob loader from astro/loaders) has a CJS-only package on its dependency chain, and content collections are evaluated in a Vite environment literally named astro — neither client nor ssr. A top-level optimizeDeps only feeds client, so that package was never pre-bundled in the astro environment, and the module runner evaluated a CJS file as ESM.

The fix is to name the environment explicitly:

optimizeDeps: { include: ['picomatch'] },                                // the client environment
environments: { astro: { optimizeDeps: { include: ['picomatch'] } } },   // the astro environment

You need both. And only astro, never ssr — writing both makes astro dev return 500 for the entire site.

Trap two: a warm cache hides the real failure

I thought I had fixed the above earlier by deleting that workaround. Dev came back immediately and the build was green.

That was a false green. node_modules/.vite is in .gitignore, so it is always warm locally, and a warm cache still holds dependencies produced by the previous build. Delete the cache and the first step fails with exactly the same error.

The only valid way to conclude “this workaround can go” is to re-verify on a cold cache: remove node_modules/.vite, or run it on a clean machine or in CI. Green locally proves nothing.

Trap three: a passing build does not mean dev runs

This project has two failure modes that look nothing alike:

  • astro build is fine end to end, but astro dev returns 500 on every request with a single line about a missing component instance.
  • Dev prints “ready”, then returns 500 on every request.

So after touching build configuration, seeing “ready” is not enough — you have to actually request a page and check for a 200.

How I upgrade now

Four steps, always in this order:

  1. Delete node_modules/.vite (and .astro), then run astro sync cold
  2. npm run check must report 0 errors / 0 warnings / 0 hints
  3. npm run build must produce the expected page count
  4. Start dev and request at least five different routes (home, a list, an article, an app, the language entry page) and confirm they all return 200

Step four is the one most likely to be skipped, and the one most likely to break. Every time I skipped it, I paid for it later.

← Back to all posts

Comments

…