How to Avoid Caching Secrets on Self-Hosted Runners
Caches and artifacts persist across jobs, so any secret written into them can be restored later by an unrelated job.
Never let credentials land in a cached directory (~/.npmrc, ~/.docker/config.json, .env). Exclude secret paths from caches and clean them up before the cache save step.
Steps
- Cache only rebuildable stores (package dirs), never credential files.
- Write auth tokens to a temp path outside the cached directory.
- Remove any credential file before the cache save step runs.
Keep credentials out of the cache
.github/workflows/ci.yml
steps:
- uses: actions/cache@v4
with:
path: ~/.npm # package store only
key: npm-${{ hashFiles('package-lock.json') }}
- run: |
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > "$RUNNER_TEMP/.npmrc"
npm ci --userconfig "$RUNNER_TEMP/.npmrc"
rm -f "$RUNNER_TEMP/.npmrc"Gotchas
- A
.npmrcwith an auth token inside a cached folder leaks to every cache hit. - Uploaded artifacts are downloadable by anyone with repo read; never put secrets in them.
Related guides
How to Clean Up Disk and Artifacts Between Runner JobsWipe the workspace, temp files, and leftover artifacts between jobs on a reused self-hosted GitHub Actions ru…
How to Prevent Secret Exfiltration on Self-Hosted RunnersStop a compromised GitHub Actions job from stealing secrets by scoping which workflows get them, masking dyna…