GitHub’s new cache-mode setting for Actions looks like a small workflow-syntax addition, but it addresses a problem that has become much harder for platform teams to ignore: CI caches are shared state, and shared state needs an access model. The September 10 changelog makes the feature generally available across GitHub plans, allowing teams to choose whether a workflow or job can restore caches, save caches, do only one of those operations, or use no cache at all.
The central shift is simple: cache access can now be treated like token permissions. Instead of assuming every job that invokes a cache action should both read and write, platform teams can grant cache capability according to trust, purpose, and blast radius. That matters because a cache is not just a performance optimization. It can influence what code runs, what dependency artifacts are reused, and how much confidence teams can place in CI results for branches, forks, release jobs, and reusable workflows.
The practical thesis for DevOps teams is that cache governance should move from implicit runner behavior to explicit pipeline policy. The work is to classify jobs by trust boundary, choose defaults that protect high-value workflows, and accept that some pipelines should trade a little speed for a clearer security posture.
What GitHub Changed
cache-mode can be set at the workflow level or on an individual job. GitHub lists four modes: read, write, write-only, and none. read allows cache restores but prevents cache saves. write allows both restores and saves. write-only allows a job to save a cache but prevents it from restoring one. none prevents cache access entirely.
The job-level setting overrides the workflow-level setting. That hierarchy is important because many repositories have one workflow file that mixes jobs with very different risk profiles: linting a pull request, building a release artifact, running integration tests, publishing a package, and calling a reusable deployment workflow can all sit in the same automation graph.
GitHub says the selected mode is enforced by the cache service through scoped cache tokens, and that the restriction carries through reusable workflows. A called reusable workflow cannot receive more cache access than the caller granted. For organizations that have standardized CI logic through reusable workflows, this is one of the most significant details. It means a central platform workflow can be useful without becoming an accidental privilege escalation path for cache access.
GitHub also calls out low-trust events such as pull_request_target. Workflows that do not declare a cache mode continue to use existing secure defaults. When a low-trust event explicitly grants write access with write or write-only, Actions adds a warning annotation because the choice can reintroduce cache-poisoning risk. That warning is not a substitute for review, but it is a useful signal for repositories where workflow changes move quickly.
Why CI Caches Deserve Their Own Permissions
Caches are usually introduced for good reasons. Dependency installs are slow. Language package managers do repeated work. Monorepos often have a large build graph where small code changes should not force every toolchain to start from cold storage. A cache can turn a frustrating pipeline into a tolerable developer experience.
That performance value is exactly why caches become security-sensitive. Developers and CI systems tend to trust what makes builds faster. If a poisoned cache can influence a later job, the attacker has found a place where speed, trust, and automation meet. The risk is not limited to one language ecosystem or one package manager. Any workflow that restores precomputed dependencies, compiled objects, tool directories, or generated state can be affected if the cache key and trust boundary are weak.
Historically, teams managed this risk with naming conventions, careful cache keys, branch scoping, and review discipline. Those controls still matter, but they are indirect. cache-mode adds a more legible permission layer above that logic.
The analogy to GITHUB_TOKEN permissions is useful. Mature Actions users already avoid broad token permissions when a job only needs read access. Cache access should follow the same pattern. A test job for an untrusted pull request may benefit from restoring a known cache but should rarely be allowed to update the cache consumed by trusted jobs. A release job may not need to restore state created by lower-trust jobs at all. A cache warm-up job may need to save but not restore. These are different capabilities, and they now have different words in workflow syntax.
A Practical Classification Model
The fastest way to misuse the new control is to choose one mode for every workflow and move on. Platform teams should instead classify jobs along two axes: how much they trust the trigger, and how much later work trusts the job’s outputs. From there, cache access becomes a policy decision rather than a local optimization.
For low-trust pull request jobs, read is often the right starting point. It preserves much of the developer-experience benefit by allowing cache restores, while preventing the job from saving new state. This is especially relevant for public repositories that accept contributions from forks, but private organizations should not dismiss the pattern.
For trusted branch builds, write may be appropriate when the workflow is responsible for refreshing caches used by ordinary CI. The key is to make that permission deliberate. If every push job can write every broadly matched cache key, teams should revisit cache-key design at the same time they add cache-mode. Least privilege does not help much when all trusted jobs share oversized cache namespaces.
For dedicated cache population jobs, write-only is the interesting new option. It lets a job save fresh cache contents without consuming an existing cache first. That can be useful when teams want a controlled path for cache updates: run a clean install, build from known inputs, publish the cache, and prevent earlier cache state from influencing the result. It is not needed everywhere, but it gives platform engineers a cleaner tool for separating cache creation from cache consumption.
For release, signing, deployment, and other high-value jobs, none deserves more consideration than it will probably get. Many sensitive jobs are not performance bottlenecks compared with the cost of a bad release. If a job signs artifacts, publishes packages, applies infrastructure changes, or deploys to production, the default question should be whether it needs cache access at all. If the answer is no, the safest cache is no cache.
Reusable Workflows Need Special Attention
Reusable workflows are one of the places where cache-mode can change operational behavior in useful ways. Organizations often centralize build, test, and deployment logic so application teams do not have to maintain every detail themselves. That centralization improves consistency, but it also creates a permissions question: what should the reusable workflow be allowed to do when called from different contexts?
GitHub’s model gives the caller a ceiling. A reusable workflow cannot receive more cache access than the caller granted. Platform teams should take advantage of that by documenting the expected cache behavior of shared workflows and providing examples for common contexts. A pull request caller might invoke the same reusable test workflow with cache-mode: read. A protected-branch caller might invoke it with write. A release caller might use none unless the build process explicitly requires cached dependencies.
This is also a good moment to audit reusable workflows that hide cache operations inside central templates. Application teams may not realize that a called workflow restores or saves caches. With the new setting, platform teams can make that behavior explicit at the call site, but they still need inventory. Search for cache actions, package-manager cache flags, and composite actions that wrap caching behavior. Then decide which callers should be allowed to preserve, update, or bypass that state.
How To Roll It Out Without Breaking CI
A sensible rollout starts with observation, not mass editing. First, identify workflows that use actions/cache, setup actions with built-in package-manager caching, or custom composite actions that write cache state. Group them by trigger: pull requests, pushes to protected branches, scheduled jobs, release tags, deployment workflows, and reusable workflow callers.
Second, set workflow-level defaults where the answer is obvious. A workflow that only validates untrusted pull requests can usually start with cache-mode: read. A workflow that exists only to publish a production release may be a candidate for cache-mode: none. Where a workflow contains a mix of jobs, override individual jobs that need more access.
Third, watch for silent performance changes. GitHub’s documentation notes that when a cache operation is not permitted by the effective mode, the cache step logs an informational message and continues. A skipped restore is treated as a cache miss, and a skipped save is simply not performed. That behavior is friendly to availability because it avoids failing workflows during rollout, but it means teams need to monitor build times and cache-hit rates rather than waiting for red pipelines.
Fourth, update contribution guidance and workflow review checklists. If a contributor proposes cache-mode: write on a low-trust trigger, reviewers should treat that with the same seriousness as broadening token permissions. GitHub’s warning annotation helps, but governance still lives in code review, branch protection, and central workflow ownership.
The Supply Chain Context
The timing of this feature fits a broader pattern in software delivery security. Recent supply-chain incidents have shown how quickly legitimate packages and ordinary install flows can become attack paths. Cloudsmith’s August analysis of compromised keyv and cacheable npm packages described malicious versions of real packages being published into the ecosystem, with install-time lifecycle scripts used to harvest credentials and propagate through maintainer accounts. Cloudsmith’s broader supply-chain guidance argues for policy-enforced control points between public registries, developers, and pipelines.
GitHub’s cache-mode is not an artifact firewall, a malware scanner, or a dependency review system. It solves a narrower problem inside Actions. But the connection is operationally important: platform teams are being pushed to govern every place where untrusted code, third-party packages, credentials, and automation state meet.
That makes cache policy part of supply-chain hygiene. A registry proxy may control which packages enter the environment. Lockfiles may control versions. Token permissions may control repository access. Cache permissions now help control whether state produced in one workflow context can be saved for another. None of these controls is sufficient by itself, but together they reduce the number of hidden trust assumptions in the delivery path.
Tradeoffs Platform Teams Should Expect
The main tradeoff is performance versus isolation. Restricting cache writes from pull request workflows may reduce cache freshness. Disabling cache access in sensitive jobs may add minutes to workflows that developers already consider slow. Using write-only for cache warmers may require more deliberate job design. These costs are real, and platform teams should measure them.
The counterweight is that cache behavior becomes easier to explain. When an incident review asks whether an untrusted pull request could have poisoned a cache used by a protected branch, teams should not have to reconstruct the answer from scattered YAML and package-manager behavior. They should be able to point to an explicit mode, a cache-key policy, and a review rule.
What To Do Next
For organizations using GitHub Actions heavily, the next step is a focused cache audit. Find the workflows that save cache state, identify which triggers can reach them, and decide whether the saved state is later consumed by more trusted jobs. Pay particular attention to public repositories, fork-based contribution models, monorepos with broad cache keys, and reusable workflows that are called from many repositories.
Then introduce cache-mode as a policy language for those decisions. Use read for low-trust validation paths that should benefit from existing caches but not update them. Use write where trusted workflows intentionally refresh caches. Use write-only for clean cache population patterns. Use none for jobs where cache state is unnecessary or not worth the additional trust assumption.
The feature is small enough to adopt incrementally, but important enough to include in platform standards. CI caches used to be treated mostly as a speed knob. GitHub’s new control makes them look more like what they have become: a governed resource in the software delivery system.


