GitHub Actions Cheatsheet
Caching
Use this GitHub Actions reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
actions/cache — Core Usage
- uses: actions/cache@v4
with:
path: ~/.npm # path(s) to cache
key: npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: | # fallback keys (prefix match, most to least specific)
npm-On cache hit: restores files and sets cache-hit: true.
On cache miss: runs the job normally; at the end, saves the path under key.
Key Design
# Language / OS / lockfile hash is the standard pattern
key: ${{ runner.os }}-node20-${{ hashFiles('**/package-lock.json') }}
# Restore keys fall back from most to least specific
restore-keys: |
${{ runner.os }}-node20-
${{ runner.os }}-Rule: key should change whenever the cached content changes. restore-keys give you a warm starting point when an exact match isn't found.
Built-in Caching via setup-* Actions
These actions handle cache automatically — prefer them over manual actions/cache:
# Node
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # npm | yarn | pnpm
cache-dependency-path: '**/package-lock.json'
# Python
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
# Go
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true # caches $GOPATH/pkg/mod
# Java / Gradle
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: temurin
cache: gradleCommon Cache Paths by Ecosystem
| Ecosystem | Path | Key ingredient |
|---|---|---|
| npm | ~/.npm | hashFiles('**/package-lock.json') |
| yarn (classic) | ~/.cache/yarn | hashFiles('**/yarn.lock') |
| pnpm | ~/.local/share/pnpm/store | hashFiles('**/pnpm-lock.yaml') |
| pip | ~/.cache/pip | hashFiles('**/requirements*.txt') |
| poetry | ~/.cache/pypoetry | hashFiles('**/poetry.lock') |
| Go modules | ~/go/pkg/mod | hashFiles('**/go.sum') |
| Gradle | ~/.gradle/caches | hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') |
| Maven | ~/.m2/repository | hashFiles('**/pom.xml') |
| Cargo/Rust | ~/.cargo/registry + ./target | hashFiles('**/Cargo.lock') |
| Bundler (Ruby) | vendor/bundle | hashFiles('**/Gemfile.lock') |
| Composer (PHP) | vendor | hashFiles('**/composer.lock') |
| Docker layers | (via BuildKit GHA cache) | — |
Checking Cache Hit
- uses: actions/cache@v4
id: cache
with:
path: node_modules
key: node-${{ hashFiles('package-lock.json') }}
- if: steps.cache.outputs.cache-hit != 'true'
run: npm ciSave Cache Even on Failure
- uses: actions/cache/restore@v4 # restore only
id: cache
with:
path: node_modules
key: node-${{ hashFiles('package-lock.json') }}
- run: npm ci && npm test # may fail
- uses: actions/cache/save@v4 # explicit save (always runs)
if: always()
with:
path: node_modules
key: ${{ steps.cache.outputs.cache-primary-key }}Docker Layer Caching
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: myapp:latest
cache-from: type=gha # restore from GHA cache
cache-to: type=gha,mode=max # save all layers (mode=max vs min)Multiple Paths
- uses: actions/cache@v4
with:
path: |
~/.npm
~/.cache/playwright
node_modules
key: ci-${{ runner.os }}-${{ hashFiles('package-lock.json') }}Scoped Keys by Branch
key: ${{ runner.os }}-build-${{ github.ref_name }}-${{ hashFiles('**/*.lock') }}
restore-keys: |
${{ runner.os }}-build-${{ github.ref_name }}-
${{ runner.os }}-build-main-
${{ runner.os }}-build-This tries the current branch first, then falls back to main's cache.
Cache Limits and Behavior
| Property | Value |
|---|---|
| Max cache size per repo | 10 GB |
| Max single cache entry | 10 GB |
| Retention | 7 days since last access |
| Eviction policy | LRU when 10 GB limit reached |
| Scope | Branch-based; PRs can read from base branch |
| Availability | First job that saves the key wins; concurrent writes are safe |
Managing Caches via CLI
# List caches gh cache list gh cache list --key npm- # Delete a cache entry gh cache delete <cache-id> gh cache delete --all # clear all caches # View cache usage gh cache list --limit 100 | awk '{sum += $4} END {print sum " bytes"}'
Cache Security Notes
- Caches are isolated by branch — forks cannot access parent repo caches.
pull_request_targetworkflows CAN share cache with the base branch — audit carefully.- Never cache secrets or credentials.