DEV Community

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'

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-

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.

Comments

No comments yet. Start the discussion.