Secrets and Variables
Store tokens and passwords as encrypted secrets and read them safely in workflows.
Never hard-code passwords or API tokens in a workflow. Store them as encrypted secrets under Settings → Secrets and variables → Actions.
Using secrets
Read a secret with ${{ secrets.NAME }}. GitHub automatically masks its value in the logs. A built-in GITHUB_TOKEN is provided for each run to interact with your repo.
Variables
Non-sensitive configuration goes in variables, read with ${{ vars.NAME }}.
Example
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Publish
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
API_URL: ${{ vars.API_URL }}
run: |
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
npm publishWhen to use it
- A team stores the production database password as a GitHub secret so it never appears in the workflow YAML file or logs.
- A developer uses a repository variable (non-secret) to store the deployment region, keeping config visible in the Actions UI.
- An organisation stores a shared Docker Hub token as an organisation-level secret so all repos can push images without duplicating the credential.
More examples
Store and use a secret in a workflow
Maps the GitHub secret into the step's environment; the value is masked in logs and never exposed in workflow YAML.
# Store via CLI first:
# gh secret set DATABASE_URL
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}Use repository variables for config
Variables (vars context) are not masked and are suitable for non-sensitive configuration values visible in the Actions UI.
# Set via CLI:
# gh variable set DEPLOY_REGION --body us-east-1
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: echo "Region: ${{ vars.DEPLOY_REGION }}"Rotate a secret without downtime
Overwriting a GitHub secret with gh secret set is instantaneous; the next workflow run picks up the new value automatically.
# Set the new value (overwrites the old secret)
gh secret set API_TOKEN --body "$NEW_TOKEN"
# Verify the secret exists
gh secret list
# NAME UPDATED
# API_TOKEN just now
Discussion