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: gradle

Common Cache Paths by Ecosystem

EcosystemPathKey ingredient
npm~/.npmhashFiles('**/package-lock.json')
yarn (classic)~/.cache/yarnhashFiles('**/yarn.lock')
pnpm~/.local/share/pnpm/storehashFiles('**/pnpm-lock.yaml')
pip~/.cache/piphashFiles('**/requirements*.txt')
poetry~/.cache/pypoetryhashFiles('**/poetry.lock')
Go modules~/go/pkg/modhashFiles('**/go.sum')
Gradle~/.gradle/cacheshashFiles('**/*.gradle*', '**/gradle-wrapper.properties')
Maven~/.m2/repositoryhashFiles('**/pom.xml')
Cargo/Rust~/.cargo/registry + ./targethashFiles('**/Cargo.lock')
Bundler (Ruby)vendor/bundlehashFiles('**/Gemfile.lock')
Composer (PHP)vendorhashFiles('**/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 ci

Save 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

PropertyValue
Max cache size per repo10 GB
Max single cache entry10 GB
Retention7 days since last access
Eviction policyLRU when 10 GB limit reached
ScopeBranch-based; PRs can read from base branch
AvailabilityFirst 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_target workflows CAN share cache with the base branch — audit carefully.
  • Never cache secrets or credentials.