How to Hand a Secret From One Step to the Next Securely in GitHub Actions
Mask the value first, then store it in GITHUB_OUTPUT; a later step reads it via the steps context, still masked.
When a step mints a token, call ::add-mask:: on it before anything else, then write name=value to $GITHUB_OUTPUT. Downstream steps read steps.<id>.outputs.<name>, which the runner keeps masked.
Steps
- Compute the secret in a step with an
id. - Echo
::add-mask::<value>before using or storing it. - Append
name=valueto$GITHUB_OUTPUT. - Read
steps.<id>.outputs.<name>in later steps viaenv:.
Workflow
.github/workflows/ci.yml
steps:
- id: token
run: |
TOKEN=$(./mint-token.sh)
echo "::add-mask::$TOKEN"
echo "token=$TOKEN" >> "$GITHUB_OUTPUT"
- run: ./call-api.sh
env:
API_TOKEN: ${{ steps.token.outputs.token }}Gotchas
- Mask before writing to
$GITHUB_OUTPUT; otherwise the value can surface in debug logs. - Step outputs do not cross job boundaries; promote to a job
outputs:entry for that (and it stays masked).
Related guides
How to Stop Secrets Leaking Into Logs in GitHub ActionsPrevent GitHub Actions from printing secrets to logs by masking dynamic values, avoiding set -x with secret a…
How to Mint a Temporary Database Password in GitHub ActionsGenerate a short-lived database credential inside a GitHub Actions job with HashiCorp Vault dynamic database…