Skip to content

How to Cache Dependencies in GitHub Actions

DodaTech 2 min read

In this tutorial, you'll learn about How to Cache Dependencies in GitHub Actions. We cover key concepts, practical examples, and best practices.

The Problem

Your GitHub Actions workflow installs dependencies from scratch on every run, taking several minutes per build. Caching restores previously downloaded packages so subsequent runs complete faster.

Quick Fix

Step 1: Cache npm dependencies

- name: Cache npm dependencies
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-

The key is derived from the lockfile hash. When the lockfile changes, the cache is invalidated and a new one is created.

Step 2: Cache pip dependencies

- name: Cache pip dependencies
  uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
    restore-keys: |
      ${{ runner.os }}-pip-

Step 3: Cache Maven dependencies

- name: Cache Maven dependencies
  uses: actions/cache@v4
  with:
    path: ~/.m2/repository
    key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
    restore-keys: |
      ${{ runner.os }}-maven-

Step 4: Full workflow with caching

name: CI
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Cache npm
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-
      - name: Install dependencies
        run: npm ci
      - name: Run tests
        run: npm test

Step 5: Cache multiple paths in one step

- name: Cache multiple paths
  uses: actions/cache@v4
  with:
    path: |
      ~/.npm
      ~/.cache/pip
      ~/.m2/repository
    key: ${{ runner.os }}-combined-${{ hashFiles('**/package-lock.json', '**/requirements.txt', '**/pom.xml') }}

Step 6: Verify cache hit/miss

In the workflow output, look for:

Cache restored from cache for key: linux-npm-abc123...
Cache miss for key: linux-npm-abc123...

Step 7: Set cache size limit

- name: Cache with size limit
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-
    max-size: 200MB

Alternative Solutions

Use language-specific caching actions like actions/setup-node with cache: npm which handles caching automatically:

- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: npm

Common Errors

Cache quota exceeded: GitHub allows 10 GB of cache per repository. Monitor usage under Settings > Actions > Caches and prune old caches with gh actions cache delete.

Cache never hits: If hashFiles includes files that change on every commit (like timestamps or build IDs), the cache is invalidated every run. Only hash files that change between dependency updates.

Cache save fails silently: If the cache step fails, the workflow continues but without caching. Check the step output for "Cache saved successfully" or error messages.

Cross-branch cache pollution: Without branch-specific keys, a cache from one branch may be restored on another. Include github.ref in the cache key to isolate by branch.

Prevention

  • Use lockfiles (package-lock.json, requirements.txt) in the cache key for automatic invalidation.
  • Set a max-size limit to avoid filling the GitHub cache quota (10 GB per repo).
  • Use restore-keys as a fallback when the exact key misses.
  • Monitor the Actions cache usage under Settings > Actions > Caches.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro