How to Avoid Caching Secrets in GitHub Actions
Cache only dependency stores you can rebuild, never config files, netrc, or token caches that hold credentials.
A cache is a tarball restorable by anyone who can run the workflow, including PRs from collaborators. Scope cached paths tightly to dependency stores so a credential file (a .netrc, a registry auth token, a kubeconfig) never rides along.
Steps
- Cache the specific store (
~/.m2/repository), not the parent that holds settings or credentials. - Never cache
~/.netrc,~/.docker/config.json, or token caches. - Inject secrets at runtime via
secrets.*, which are masked and never written to a cache.
Workflow
.github/workflows/ci.yml
steps:
- uses: actions/checkout@v4
# Good: cache only the artifact repository, which holds no credentials.
- uses: actions/cache@v4
with:
path: ~/.m2/repository
key: maven-${{ hashFiles('**/pom.xml') }}
# Inject the token at runtime instead of caching it.
- run: mvn -B deploy
env:
MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }}Gotchas
- Cache restore happens before your steps, so a poisoned cache entry can run before any check; keep keys tight and inputs hashed.
- Caches created on a PR are scoped to that PR, but a leaked token in a default-branch cache is readable by every PR.
Related guides
How to Cache the Maven Repository in GitHub ActionsCache the local Maven repository (~/.m2/repository) in GitHub Actions keyed on the pom.xml files, so mvn buil…
How to Cache Docker Layers to a Registry in GitHub ActionsCache Docker build layers to a container registry in GitHub Actions with Buildx cache-to type=registry, so la…