Speed up GitHub Actions with dependency caching
Cache node_modules, pip wheels, and Go modules keyed on your lockfile hash, and warm the cache on main to cut minutes off every GitHub Actions run.
Every push that runs npm ci from a cold cache is paying for the same download twice — once in wall-clock time, once in billed minutes. GitHub Actions caching stores directories like ~/.npm, pip wheels, or Go module caches keyed on your lockfile hash and restores them on the next run. A one-line cache: npm on setup-node often cuts dependency install from minutes to seconds without changing what your pipeline actually installs.
You will key caches on lockfiles (not commit SHA alone), use restore-keys for partial hits, prefer caching download dirs over node_modules, and understand why a cache written on a feature branch can be restored by pull request jobs. The same discipline applies in GitLab cache keys — patterns covered in Software supply chain security alongside scanning and signing gates.
The cache key decides reuse. Change the lockfile and you get a miss — which is exactly what you want when dependencies actually changed.
Key the cache on your lockfile
The actions/cache key should change when dependencies change and stay stable when only application code changes. Hash package-lock.json, poetry.lock, or go.sum — not the entire repo. Add restore-keys with a prefix so a near-miss still restores most of the tree while the exact key repopulates on save.
- uses: actions/cache@v4with:path: ~/.npmkey: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}restore-keys: |npm-${{ runner.os }}-
The setup actions already do this
For Node, Python, Go, and Rust the official setup-* actions can restore and save caches automatically when you pass cache: npm (or pip, go, cargo). You still run a real install step — postinstall scripts and platform-specific binaries stay correct because you are not blindly copying a whole node_modules directory from another runner.
- uses: actions/setup-node@v4with:node-version: 20cache: npm
first run (cache miss):npm ci ............ 1m48ssecond run (cache hit):npm ci ............ 12sgrep hashFiles .github/workflows/ci.ymlkey changes only when package-lock.json changesWarm main so feature branches start fast
A brand-new branch always misses until someone pushes a lockfile change on that branch. Run a scheduled workflow on main that restores and saves caches so the first PR from a fork or feature branch gets a restore-keys partial hit instead of a full cold download. Keep cache paths scoped to dependency stores — never cache build outputs you treat as trusted artifacts.
Where this goes next
Faster installs free minutes for the gates that matter: SAST, secret scanning, SBOM generation, and Cosign signing. Wire those into the same workflow template and pin action SHAs so speed never trades away supply-chain hygiene. Software supply chain security walks from commit to admitted artifact — caching is the efficiency layer that keeps those gates affordable at scale.
Go deeper in a courseSoftware supply chain securityPipeline efficiency, scanning, SBOMs, signing, and org-wide secure defaults.View course