Caching Dependencies in GitHub Actions

A
Author
11 Sep, 2026 1 min read 608 views

Installing dependencies is often the slowest part of a CI run. Caching downloads between runs can cut minutes from every workflow.

The easy way: built into setup actions

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: npm               # also: yarn, pnpm

- uses: actions/setup-python@v5
  with:
    python-version: '3.13'
    cache: pip

- uses: actions/setup-go@v5
  with:
    go-version: stable       # module and build caches on by default

These cache the package manager's download cache, keyed on your lockfile.

Custom caches with actions/cache

- name: Cache Next.js build cache
  uses: actions/cache@v4
  with:
    path: .next/cache
    key: nextjs-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-${{ hashFiles('src/**') }}
    restore-keys: |
      nextjs-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-
      nextjs-${{ runner.os }}-
  • key must match exactly for a cache hit.
  • restore-keys are prefixes used as fallbacks: a partial cache is better than none.
  • Caches are saved at the end of a successful job when there was no exact hit.

Docker layer caching

- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
  with:
    context: .
    push: false
    cache-from: type=gha
    cache-to: type=gha,mode=max

Things to know

  • Cache storage per repository is limited; old entries are evicted when you go over, and unused caches expire after a week.
  • Pull requests can read caches from the base branch, but caches they create are scoped to the PR.
  • Cache downloads, not node_modules: npm ci deletes node_modules anyway, and restoring the npm cache is more reliable across Node versions.
  • Never cache anything that contains secrets.
Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.