Speed Up CI with GitHub Actions Dependency Caching
Learn how to use GitHub Actions' cache action to reuse npm, pip, or Maven dependencies across workflow runs, reducing CI time and staying within storage limits.
10 Apr 2026, 00:33 UTC

The problem: repeated dependency downloads
Every time a workflow runs, CI systems fetch the same npm, pip, or Maven packages from remote registries. For projects with large dependency trees this can add several minutes to each run, slowing feedback for developers and increasing minutes‑used quotas.
Thesis: a simple cache step can cut restore time dramatically
GitHub Actions provides the actions/cache action. By storing the dependency directory after the first successful install and restoring it on later runs when the lockfile hash matches, you avoid re‑downloading unchanged packages.
How to set up the cache step
Add a cache step before your install command. The action needs a key that changes whenever the lockfile changes, and one or more restore‑keys that allow falling back to a slightly older cache when an exact match is missing.
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache node_modules
uses: actions/cache@v3
with:
# path to cache
path: ~/.npm
# key based on lockfile hash
key: npm-${{ hashFiles('package-lock.json') }}
# fallback keys: allow any previous npm cache
restore-keys: |
npm-
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
The path points to the directory that holds the downloaded packages. For npm the default location is ~/.npm; for pip you might cache ~/.cache/pip, and for Maven ~/.m2/repository.
Worked example: Node.js project
Consider a repository with a package-lock.json that resolves to ~120 MB of modules. Without caching, each workflow spends ~90 seconds in npm ci. After adding the cache step above:
- On the first run the cache is missed; the action saves ~120 MB to the cache storage.
- On subsequent runs with an unchanged lockfile, the restore step finds a hit, restoring the directory in ~15 seconds, and the install step becomes a no‑op, cutting the total job time by roughly a minute.
You can see the outcome in the workflow run UI: the “Cache” step shows “Cache hit” and reports the size restored.
Trade‑offs and limits
GitHub Actions caching is convenient but not free:
- Storage quota: each repository receives 10 GB total for all caches. Large binary artifacts (pre‑built native libraries, big datasets) can quickly consume this limit.
- Stale dependencies: if the key does not change (e.g., you forget to include a lockfile hash), the action may restore an outdated
node_modulesdirectory, leading to flaky builds. Always include a deterministic hash of the exact file(s) that determine dependencies. - Per‑cache size limit: a single cache entry cannot exceed ~2 GB. Larger directories should be split or stored elsewhere, such as GitHub Packages.
Checking and maintaining cache effectiveness
After adding the cache step, verify that it is helping:
- Compare workflow durations: look at the total job time before and after the cache step in the “Actions” tab.
- Inspect the run UI: open a completed workflow, expand the “Cache” step, and confirm it reports a hit, the key used, and the restored size.
- Optional API check: run
GET /repos/{OWNER}/{REPO}/actions/cacheswith a personal token that hasreposcope. The response lists each cache, its size, and the key; ensure the total size stays under the 10 GB limit.
To stay within limits, periodically delete old caches via the same API (DELETE /repos/{OWNER}/{REPO}/actions/caches/{CACHE_ID}) or use a workflow that runs on a schedule to prune caches older than a certain date.
Actionable closing
Add the cache step before your dependency install command, monitor hit/miss rates in each workflow run, and set up a monthly cleanup job to keep storage usage in check. With these steps you’ll see faster CI feedback without sacrificing correctness.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.