Why is npm install slow in GitHub Actions?
How do I speed up npm install in GitHub Actions?
npm install is slow in GitHub Actions because each job starts on a fresh machine with an empty npm cache, so every run downloads and unpacks every package again, and unpacking is thousands of small file writes on a 2 vCPU runner’s disk. Fix it with npm ci and the setup-node cache (cache: npm, keyed on package-lock.json), or cache node_modules itself and skip the install when the lockfile has not changed. Then give the install a faster disk: Sengi runs on AMD Ryzen 9 7950X3D bare metal boosting past 5 GHz with local NVMe at 15× the random-read speed of GitHub’s runner, from $0.003 a minute.
Last checked October 23, 2026. Disk and job figures measured on Sengi in October 2026 against GitHub’s runner of the same size.
What are the ways to cache npm in GitHub Actions?
| Approach | What it caches | Install on a cache hit |
|---|---|---|
cache: npm on actions/setup-node | npm’s download cache | Still runs; no downloads |
actions/cache on node_modules | The installed tree | Skipped |
| Neither | Nothing | Full download and unpack |
The simplest, safe default:
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
Skipping the install when nothing changed:
- uses: actions/cache@v4
id: modules
with:
path: node_modules
key: modules-${{ runner.os }}-node22-${{ hashFiles('package-lock.json') }}
- if: steps.modules.outputs.cache-hit != 'true'
run: npm ci
How much does a faster disk help?
An install that misses the cache is disk-bound: npm writes tens of thousands of small files. Sengi’s runners use local NVMe at 296k random-read IOPS against 19.6k on GitHub’s 4 vCPU runner, and 3× the random writes. On the same 2 vCPU as ubuntu-latest, measured October 2026, a Vite build and test took 328 s on Sengi against 552 s on GitHub, and a Node and Docker build 299 s against 539 s. On Sengi, actions/cache and the setup-node cache are served from Sengi’s own cache with no workflow changes, included. More fixes in how to speed up GitHub Actions; providers side by side in the runner comparison.
Frequently asked questions
Should I use npm ci or npm install in GitHub Actions?
npm ci. It installs exactly what package-lock.json lists, fails if the lockfile and package.json disagree, and starts from a clean node_modules, which makes CI installs faster and reproducible. npm install may rewrite the lockfile and resolve versions again.
Should I cache node_modules or the npm cache?
The setup-node cache stores npm's download cache, which is safe across Node versions but still runs npm ci to unpack every package. Caching node_modules itself, keyed on the lockfile and the Node version, skips the install entirely on a hit, at the cost of a bigger cache.
Does setup-node caching work with pnpm and Yarn?
Yes. Set cache: pnpm or cache: yarn on actions/setup-node; for pnpm, install pnpm first with pnpm/action-setup so setup-node can find its store.
Why is restoring the npm cache itself slow?
A large cache takes time to download and unpack, and the unpack is thousands of small file writes. Keep the cache to what you reuse, key it on the lockfile so it is replaced rather than grown, and run on a fast disk: Sengi's local NVMe reads at 15× the random-read speed of GitHub's runner.
The fastest hardware for your GitHub Actions.
AMD Ryzen 9 7950X3D desktop CPUs boosting past 5 GHz, on bare metal with local NVMe, a fresh VM for every job, and 2 vCPU jobs ready in 2 seconds. Builds finish up to 1.9× faster than on GitHub’s runners, and at up to half GitHub’s price per minute they cost up to 74% less per build. Change one runs-on line, or give your coding agent one prompt and it opens the pull request for you.
