GitHub Actions cache not working
A cache that never hits is worse than none: you pay the upload and still wait for a cold install every run.
1. There is no cache at all
The setup actions ship caching, and it is off unless you ask for it:
- uses: actions/setup-node@v4
with:
node-version: 20
+ cache: npm
Equivalent inputs exist for setup-python (pip, poetry, pipenv) and setup-java (gradle, maven). This is the most common cause and the cheapest fix.
2. No lockfile, so there is nothing to key on
cache: npm hashes your lockfile to build the key. Without package-lock.json the action cannot compute a key and caching silently does nothing. Commit the lockfile. The same applies to poetry.lock, Pipfile.lock, yarn.lock and pnpm-lock.yaml.
3. A key with something volatile in it
- key: ${{ runner.os }}-node-${{ github.run_id }}
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
run_id, run_number and timestamps change on every run, so the key never matches and the cache is rebuilt every time. A key should be a function of the inputs, not of the run.
4. A monorepo looking in the wrong place
In a workspace repository the lockfile is often not at the root. Point the action at it:
with:
cache: npm
+ cache-dependency-path: apps/web/package-lock.json
Without this, the key hashes a file that does not exist and the cache never hits.
5. Expecting a hit on a different branch
Caches are scoped to the branch that created them. A pull request can restore the base branch's cache, but the base branch cannot restore a pull request's cache, and a brand-new branch starts cold. Also worth knowing: caches not read for seven days are evicted, and a repository has a total cache budget of about 10 GB, after which older entries are removed. A cache that worked last month may have been evicted rather than broken.
Confirm it actually worked
Look for a Cache restored line in the step output, and check that the install step got materially shorter. Then measure over at least ten comparable runs: a cold first run is expected and is not a regression.
CIPatch reads your workflow and lockfiles and reports whether a safe cache is missing or misconfigured. The scan is read-only, needs no account for the first repository, and changes nothing.