CalcSnippets Search
CI/CD 3 min read

GitHub Actions With `pnpm` Cache: The CI Setup That Stops Reinstalling the World

A practical GitHub Actions guide showing how to use setup-node with pnpm dependency caching, avoid slow reinstall loops, and understand what caching is actually improving in CI.

Why CI feels slower than it should: teams often accept reinstalling dependencies on every run as normal friction, even when GitHub Actions can cache enough state to make the feedback loop noticeably better.

What the problem looks like

A typical Node workflow in GitHub Actions often starts by reinstalling dependencies from scratch:

- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: pnpm install
- run: pnpm test

That works, but in busy repositories it burns time over and over.

The right mental model for caching

Caching does not mean CI stops caring about dependency correctness. It means the workflow can reuse package-manager state when the inputs have not changed meaningfully.

If you are using pnpm, the easiest path is to let setup-node handle package-manager-aware caching.

A cleaner GitHub Actions setup

name: ci

on:
  push:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - run: corepack enable
      - run: pnpm install --frozen-lockfile
      - run: pnpm test

That one cache: pnpm line is doing more useful work than many teams realize.

Why --frozen-lockfile still matters

Caching is about speed. --frozen-lockfile is about discipline.

You still want CI to fail if the dependency graph in the repo and the lockfile are out of sync. Fast and trustworthy are not opposing goals here.

Why people think the cache is “not working”

Usually one of these is true:

  1. the lockfile keeps changing
  2. the workflow does not actually use the expected package manager
  3. Node versions differ in a way that changes the cache context
  4. the install step is doing more work than dependency restoration alone

That last one is important. A large workspace may still spend meaningful time linking packages, running scripts, or rebuilding native dependencies. Caching helps, but it does not turn every install into zero work.

A healthy CI stack

The most reliable pattern is:

  1. pin Node
  2. enable Corepack if needed
  3. use a locked package-manager workflow
  4. let setup-node cache the package-manager state

That gives you a decent mix of reproducibility and speed without a lot of hand-rolled YAML complexity.

Where teams still overcomplicate this

They add custom cache keys, extra restore logic, or a second caching layer before measuring whether the supported setup already solved most of the pain. That is usually backwards. CI should become simpler first, then faster where needed, not more clever by default.

Final recommendation

If your GitHub Actions workflow spends too much time reinstalling dependencies, start with the obvious supported path before inventing your own cache system. actions/setup-node plus cache: pnpm is one of those rare CI improvements that is both simple and worth doing.

It also makes your CI story easier to explain to the next person who has to maintain it. That alone is a meaningful engineering advantage over clever but fragile custom caching setups.

That kind of maintenance clarity compounds over time.

It means the workflow stays understandable even after the original author has moved on.

That is exactly the sort of benefit teams feel months later when the pipeline still makes sense.

Sources

Keep reading

Related guides