How to Avoid Re-Downloading Dependencies in CI
Re-downloading the same packages on every cold runner is the most common avoidable CI cost. Caching the store fixes it across ecosystems.
The pattern is the same in every language: a cold runner fetches every dependency from a registry. Caching the package store keyed on the lockfile, then installing offline, eliminates the repeat download.
1. Cache the package store, keyed on the lockfile
Cache the ecosystem store (not the project tree) keyed on the lockfile so an unchanged dependency set restores instead of re-downloading.
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: npm-2. Install offline against the cache
Prefer the offline path so a warm cache never hits the network.
- run: npm ci --prefer-offline --no-audit3. Proxy the registry for big or private deps
When you cannot cache everything (large binaries, private registries), a pull-through registry proxy keeps a local copy so the second fetch is local. This complements, not replaces, the store cache.
4. Keep deps warm on the runner
Latchkey managed runners can keep common dependency stores warm on the image, so the most-used packages are already present and even the cache restore is skipped.
Key takeaways
- Cache the ecosystem store keyed on the lockfile, across every language.
- Install offline so a warm cache never touches the network.
- Use a registry proxy for deps too large or private to cache directly.