Log In

How GitHub Actions dependency caching works

GitHub Actions dependency caching stores files such as package directories between workflow runs so later jobs avoid repeating slow install steps. The action matches a primary key first; when that key misses, it falls back to restore keys that match a prefix. This design lets teams reuse a recent cache even after small dependency changes, cutting build time without manual intervention. The cache is scoped to the key, version, and branch, with the default branch cache visible to other branches. A repository can hold up to 10 GB of caches; entries untouched for seven days are evicted automatically. Because caches are immutable, any change to the key creates a new entry rather than updating an existing one.

How cache keys control exact matches

A cache key is an explicit string that identifies a cache entry. You build the key from context values such as runner.os, hashFiles of a lockfile, or commit SHA. When the key matches an existing entry exactly, the action restores the cached files and sets the cache-hit output to true. Subsequent steps can then skip install commands. If the key does not match, the step reports a miss and the workflow continues to the next restore-keys entry or creates a fresh cache at the end of the job.

Note Assigning different path values for save and restore steps produces a cache miss. Use the same path list in both restore and save actions.

Restore keys provide fallback matches

Restore keys are an ordered list of prefix strings. When the primary key misses, the action tries each restore key in order and restores the most recent cache whose key begins with the prefix. This mechanism supplies a usable set of dependencies even after a lockfile change, so only the delta needs to be downloaded. The restored cache is still read-only; a new cache is saved only under the original primary key if the job succeeds.

Cache invalidation and updates

Because every cache entry is immutable, changing the primary key is the only way to create a new cache. When a lockfile hash changes, the next run uses a new key and saves a fresh cache. If you need to refresh a cache on every commit, generate a unique key with github.run_id and rely on restore keys to pull the previous state. This approach consumes quota quickly but guarantees the latest files are cached. Read-only tokens, common on pull requests from forks, prevent saves while still allowing restores.

Common stale-cache mistakes

Teams often include volatile values such as github.run_number in the primary key, producing a cache miss on every run. Another frequent error is changing the path list between restore and save steps, which silently creates a new cache instead of reusing the intended one. Overly broad restore keys can pull an ancient cache that no longer contains current transitive dependencies, leading to subtle test failures. Finally, relying on a single job to populate caches for the entire repository can hit rate limits or token restrictions when that job runs from a fork.

Practical workflow patterns

A common pattern restores with a lockfile hash key and a short restore-keys prefix, then installs only on cache miss. Another pattern uses a centralized cache job that runs on the default branch with write permissions while other jobs use read-only tokens. When lockfiles are generated during the build, compute the key inside the save step rather than the restore step. Always place the cache action before any step that needs the cached files.

Sources

See our free AI tools →