DEV Community

A.F.
A.F.

Posted on • Originally published at gitspider.com

The one-line GitHub Actions cache most repos forget

The first thing I check on a slow CI run is the cache, and the cache is usually missing. It is the cheapest speedup there is: one line on your setup step, and every run stops re-downloading and rebuilding the exact dependencies it already had yesterday. On a typical Node or Python project that is 30 to 90 seconds back, every run, forever. People skip it anyway, then pay GitHub to reinstall lodash for the ten-thousandth time.

The free win: built-in caching

You probably do not need actions/cache directly. The setup-* actions cache your package manager for you, you just have to turn it on:

# Node
- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'npm'        # or 'yarn' / 'pnpm'

# Python
- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'
Enter fullscreen mode Exit fullscreen mode

Go (actions/setup-go) caches by default. Java (actions/setup-java) needs cache: 'gradle' (or 'maven') set explicitly. Check your setup step first, the one-line fix covers most projects.

Custom caching with actions/cache

For anything the setup actions do not cover (a build output, a tool the package manager does not own), reach for actions/cache with an explicit path and key:

- uses: actions/cache@v4
  with:
    path: ~/.cache/my-tool
    key: ${{ runner.os }}-mytool-${{ hashFiles('**/lockfile') }}
    restore-keys: |
      ${{ runner.os }}-mytool-
Enter fullscreen mode Exit fullscreen mode

Get the key right, or the cache never helps

The key should change only when your dependencies change, which is why it hashes the lockfile (hashFiles('**/package-lock.json')). The restore-keys prefix lets a run fall back to the most recent matching cache when the exact key misses, so a single dependency bump restores most of the cache instead of rebuilding everything.

Why your cache "isn't working"

  • Caching the wrong directory: cache the package manager's store, not node_modules on most setups.
  • A key with no lockfile hash, so it never invalidates and you keep serving a stale cache.
  • Cache scope: a branch reads its own cache plus the default branch's, but not a sibling branch's, so cross-branch hits can surprise you.

Find out if you're missing it

astro, for instance, has a release workflow with no dependency cache. Curious about yours? See real scorecards on the showcase, then scan your own.


I build GitSpider, which scans a repo's GitHub Actions and flags the missing cache (and the rest) with the fix for each. Free, no install for public repos: gitspider.com/scan.

Top comments (0)