Skip to content
Latchkey

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

Run this faster and cheaper on Latchkey managed runners. Start free →