The biggest win for Astro build speed in CI is caching node_modules/.astro between runs. That’s Astro’s default cacheDir, where it keeps optimised images and the content layer’s data store. Without it, every CI build re-processes every image from scratch. Add a dependency cache for npm, and a typical image-heavy site goes from rebuilding everything to only processing what changed.
Key Takeaways
- Cache
node_modules/.astro(or your customcacheDir) between builds.- Cache npm’s download cache and use
npm ci.- Images are usually the slowest part: avoid unnecessary formats and oversized sources.
- Measure first:
astro buildprints timings per phase.
Find what’s slow
Run a build and read the log. Astro reports time spent generating static routes and optimising images:
npm run build
If most of the time is in image generation, caching will help a lot. If it’s in route generation, look at expensive work in getStaticPaths() or page frontmatter, such as one API call per page instead of one for the whole collection.
GitHub Actions
# .github/workflows/build.yml
name: Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- name: Cache Astro assets
uses: actions/cache@v4
with:
path: node_modules/.astro
key: astro-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-${{ github.sha }}
restore-keys: |
astro-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-
astro-${{ runner.os }}-
- run: npm run build
npm ci deletes node_modules before installing, so restore the Astro cache after it, as shown. The key includes the commit SHA so each run saves a fresh cache, while restore-keys picks up the most recent one.
Netlify, Vercel and Cloudflare Pages
Hosts cache dependencies between builds automatically. Whether node_modules/.astro survives depends on the platform and changes over time. If builds are slow on a host, move cacheDir to a folder the host is documented to persist, or use the host’s build plugin or cache API. Check your host’s build cache documentation for the current behaviour.
// astro.config.mjs
export default defineConfig({
cacheDir: './.astro-cache',
});
Reduce image work
Caching helps repeat builds. These help every build:
- Resize sources before committing. A 6000px camera original takes far longer to process than a 2400px one, and nobody needs the difference on the web.
- Limit formats.
<Picture formats={['avif', 'webp']} />encodes twice per width. AVIF encoding is slow; use it where the savings matter, such as heroes. - Limit widths. A
layoutgenerates several widths per image; for small thumbnails, afixedlayout or explicit shortwidthslist is enough. - Use the CMS’s image CDN for remote images instead of re-processing them in Astro.
Concurrency
By default Astro builds pages one at a time. If page generation is the slow part and your pages do independent async work, raise it:
export default defineConfig({
build: { concurrency: 4 },
});
This doesn’t help pages that are CPU-bound, and can increase memory use. Try it, measure, and keep it only if the build gets faster.
Content collections
The content layer stores parsed entries in its data store in the cache directory, and only re-parses changed files when the cache is present. For custom loaders that fetch from an API, use an object loader that compares a digest or updatedAt per entry, so unchanged entries are skipped. The content collections guide covers loaders.
Keep CI and local in sync
- Pin Node with
.nvmrc(22) andenginesinpackage.json. - Commit
package-lock.jsonand always install withnpm ciin CI. - Run
astro checkas a separate step so type errors fail fast, before the expensive build.
For hosts and deploy settings, see the Astro deploy guide. Responsive image settings that affect build time are covered in responsive images with widths and sizes. The cacheDir and build.concurrency options are documented in the configuration reference.
FAQ
Why is my Astro build slow?
Usually image processing: many large source images, multiple formats and many widths per image. Cache node_modules/.astro and resize sources before committing.
Is it safe to delete node_modules/.astro?
Yes. It’s a cache. Astro rebuilds it on the next build, which will just take longer.