Skip to content
Latchkey

pnpm store not cached (slow install every run) in CI

pnpm keeps downloaded packages in a content-addressable store and hard-links them into node_modules. If that store is not cached across CI runs, every job re-fetches every package from the registry, so installs are slow and registry-flake-prone.

What this error means

pnpm install takes minutes on every run with "Progress: resolved ... downloaded ..." for the full dependency set, even when nothing changed, because the store starts empty each time.

pnpm
Packages: +812
Progress: resolved 812, reused 0, downloaded 812, added 812, done

Common causes

The content-addressable store is not persisted

With reused 0, downloaded 812, the store began empty, so pnpm fetched every package from the registry instead of reusing cached content.

The wrong cache path or key was used

Caching node_modules instead of the pnpm store, or using a key that never hits, means the store is effectively cold every run.

How to fix it

Cache the pnpm store via setup-node

setup-node with cache: pnpm locates and caches the store keyed on the lockfile automatically.

.github/workflows/ci.yml
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: pnpm

Or cache the store path directly

  1. Get the store path with pnpm store path.
  2. Cache that directory keyed on pnpm-lock.yaml.
  3. A hit shows "reused" instead of "downloaded".
.github/workflows/ci.yml
- run: echo "STORE=$(pnpm store path)" >> $GITHUB_ENV
- uses: actions/cache@v4
  with:
    path: ${{ env.STORE }}
    key: pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}

How to prevent it

  • Cache the pnpm store, not node_modules, keyed on the lockfile.
  • Prefer cache: pnpm in setup-node so the path is discovered for you.
  • Watch the "reused" count to confirm the store cache is warm.

Related guides

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