GitHub Actions Cache Limit, Size and Eviction
The GitHub Actions cache limit surprises many teams: a repository gets 10 GB by default, and once it is full, GitHub evicts older caches, which can make your builds slow again for no obvious reason. This guide is about limits, eviction, cache size, and the pnpm and Docker specifics. For the basics of actions/cache, read the caching guide first; this one starts where it stops. Numbers below come from GitHub's dependency caching docs, checked 2026-10-11.
What the limits are
- Total size per repository: 10 GB by default across all caches. Owners and administrators can raise it, and storage beyond 10 GB is billed.
- Retention: a cache entry that has not been accessed for 7 days is removed.
- Eviction: when the repository is over its limit, GitHub still saves the new cache, then evicts caches (least recently used first) until the total is under the limit.
- Number of caches: no limit on count, only on total size.
Cache usage is visible under the repository's Actions, Caches page, and with the CLI: gh cache list --sort size_in_bytes.
Why eviction makes CI slower
Eviction can cause cache thrashing: each run saves a new cache, which evicts another, which then misses on the next run. Signs: "Cache not found for input keys" in logs on branches that ran recently, or hit rate that drops as the team grows.
Common causes:
- A new key per commit. Keys like
${{ github.sha }}create one cache per push. They fill 10 GB quickly and are never hit. - Large caches duplicated per OS or matrix cell. 6 matrix cells times 700 MB is 4.2 GB.
- Caching
node_modulesor build directories that change on every lockfile edit, so each change adds another full copy. - Branch scoping. A workflow can restore caches created on the current branch, the base branch, or the default branch, and not on sibling branches. Pull request caches are created for the merge ref, so a cache made in one PR is not visible to another PR. Warm the default branch so everyone can restore from it, for instance with a scheduled job (see cron schedules).
Fixes
Keys that hit
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-${{ runner.os }}-${{ hashFiles('**/requirements*.txt') }}
restore-keys: |
pip-${{ runner.os }}-
Key on the lockfile, not the commit. restore-keys lets a partial match restore the most recent similar cache, and then a new one is saved under the exact key. The cache key builder generates these.
Save less, and save once
- Cache the package manager's download cache, not the installed tree, where possible. It is smaller and reusable across Node or Python versions.
- Use
actions/cache/restorein most jobs andactions/cache/savein one job (for example on pushes to main), so PR runs read but do not each write new entries. - Delete stale caches on PR close with
gh cache deleteto free space for the default branch.
- uses: actions/cache/restore@v4
with: { path: ~/.cache/pip, key: pip-${{ hashFiles('requirements.txt') }} }
Check cache size before saving
Large entries are slow to save and restore. If restoring takes longer than the install it replaces, drop the cache. A 1.5 GB restore over the network can take longer than a fast package install, which is a common reason for "GitHub Actions cache slow".
Cache v4 notes
actions/cache@v4 uses GitHub's newer cache service, and GitHub has deprecated the older action versions that used the legacy cache backend, so old pins can fail. If your workflow uses one, upgrade to @v4. v4 also exposes cache-hit as an output: true only on an exact key match, which lets you skip steps such as npm ci when you cache node_modules. Check the action's README for current inputs.
pnpm specifics
pnpm keeps one content-addressed store and hard-links files into node_modules. Cache the store, not node_modules:
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
setup-node with cache: pnpm caches the store keyed on pnpm-lock.yaml. If you cache manually, get the path with pnpm store path. Restoring the store and running pnpm install --frozen-lockfile --prefer-offline is typically quick because it only links files. In a monorepo, one lockfile means one key, which keeps cache count low.
Docker image caching
Caching Docker layers with actions/cache can use many gigabytes and hit the 10 GB cap. Options:
type=ghacache in Buildx stores layers in the same cache service and counts against the same limit. Usemode=minto store only the final image layers, rather thanmode=max, if size is a problem.- A registry cache (
type=registry) stores layers in your container registry instead, outside the 10 GB limit. - Order the Dockerfile so dependency layers come before source copies, so the expensive layers rarely change.
- uses: docker/build-push-action@v6
with:
cache-from: type=gha
cache-to: type=gha,mode=min
More options are in Docker layer caching in CI.
Does cache storage cost money?
Within the default limit, no extra charge. Increasing the limit beyond 10 GB bills the extra storage used. See GitHub Actions pricing explained for the other line items and the cost calculator.
When caches are not the answer
If caches are large and restores slow, a runner with local persistent disk or a faster network may help more than tuning keys. Compare in faster GitHub Actions runners. More fixes are in the speed-up guide and the slow CI hub.
FAQ
What is the GitHub Actions cache size limit?
10 GB per repository by default, across all caches. Admins can raise it, and usage above 10 GB is billed.
How long does GitHub keep a cache?
Entries not accessed for 7 days are removed. Entries are also evicted when the repository exceeds its size limit.
Why is my GitHub Actions cache not restoring?
Check that the key matches, that the cache was created on a branch you can read from, that it was not evicted, and that you are on actions/cache@v4.
Should I cache pnpm node_modules?
Cache the pnpm store instead, using setup-node with cache: pnpm.
Made by compiler.dev. Free tools · Pricing