Skip to content
Latchkey

GitHub Actions Caching node_modules Still Reinstalls Everything

Caching node_modules does not speed up the build because npm ci deletes node_modules before installing, or because a restored node_modules is not trusted. Caching the package-manager download cache plus npm ci is the reliable pattern.

What this error means

A node_modules cache restores successfully but the install step still does full work, so the cache adds upload/download time without saving install time.

.github/workflows/ci.yml
# node_modules restored, then:
- run: npm ci   # npm ci removes node_modules and reinstalls anyway

Common causes

npm ci wipes node_modules

npm ci deletes node_modules before installing for a clean, reproducible tree, so a cached node_modules is discarded and provides no benefit.

Restored tree not actually reused

Tools and lockfile changes can invalidate a restored node_modules, and a partial restore can be slower or riskier than a clean install.

How to fix it

Cache the download cache, run npm ci

Cache ~/.npm keyed on the lockfile and run npm ci - the install reuses cached tarballs but still produces a clean tree fast.

.github/workflows/ci.yml
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-
- run: npm ci

Or use setup-node built-in caching

  1. Set cache: npm on actions/setup-node to cache the download cache automatically.
  2. Keep running npm ci for a reproducible install.
  3. Only cache node_modules directly if you also use npm install (not ci) and key strictly on the lockfile.

How to prevent it

  • Cache ~/.npm (the download cache), not node_modules, with npm ci.
  • Use setup-node cache: npm for the common case.
  • Key the cache on the committed lockfile hash.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →