Caching Dependencies in GitHub Actions
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 }}-
keymust match exactly for a cache hit.restore-keysare 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 cideletesnode_modulesanyway, 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.