How to Use GitHub Secrets
Secure your CI/CD workflows with encrypted secrets
TL;DR
TL;DR: Go to Repository Settings → Secrets and variables → Actions. Add secrets with descriptive names like STRIPE_SECRET_KEY . Access them in workflows using $ . GitHub automatically masks secret values in logs.
Types of GitHub Secrets
| Type | Scope | Best For |
|---|---|---|
| Repository secrets | Single repo | Project-specific keys |
| Environment secrets | Specific environment | Production vs staging keys |
| Organization secrets | Multiple repos | Shared API keys across projects |
Step-by-Step Setup
Open repository settings
Go to your GitHub repository and click Settings → Secrets and variables → Actions.
Create a new secret
Click New repository secret and enter:
- Name: Use SCREAMING_SNAKE_CASE (e.g.,
STRIPE_SECRET_KEY) - Value: The actual secret value
Click Add secret to save.
Use secrets in your workflow
Reference secrets in your .github/workflows/*.yml files:
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: vercel --prod --token=$VERCEL_TOKEN
- name: Run tests with API key
env:
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
run: npm test
Common Workflow Examples
Deploy to Vercel
- name: Deploy
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
run: |
npm i -g vercel
vercel pull --yes --token=$VERCEL_TOKEN
vercel build --prod --token=$VERCEL_TOKEN
vercel deploy --prebuilt --prod --token=$VERCEL_TOKEN
Push to Docker Registry
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: user/app:latest
Environment-Specific Secrets
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy
env:
# Uses staging-specific secret
API_KEY: ${{ secrets.API_KEY }}
run: ./deploy.sh
deploy-production:
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy
env:
# Uses production-specific secret
API_KEY: ${{ secrets.API_KEY }}
run: ./deploy.sh
Setting Up Environments
For different secrets per environment (staging, production):
- Go to Settings → Environments
- Create environments (e.g., "staging", "production")
- Add secrets to each environment
- Reference the environment in your workflow with
environment: production
Pro tip: Add protection rules to production environments, like requiring manual approval before deploying.
Security Best Practices
- Use specific names:
STRIPE_SECRET_KEYnotAPI_KEY - Limit scope: Use repository secrets unless you need org-wide access
- Rotate regularly: Update secrets periodically and after any exposure
- Never echo secrets: Don't use
echo $SECRETin workflows - Be careful with forks: Secrets aren't available in fork PRs by default
Secrets and Pull Requests
For security, secrets are not passed to workflows triggered by pull requests from forks. This prevents malicious PRs from stealing your secrets. Use pull_request_target carefully if you need secrets in PR workflows.
Using GitHub CLI
# Set a secret
gh secret set STRIPE_SECRET_KEY
# Set from a file
gh secret set GOOGLE_CREDENTIALS < credentials.json
# List secrets (names only, not values)
gh secret list
# Delete a secret
gh secret delete OLD_SECRET
# Set an environment secret
gh secret set API_KEY --env production
Can I see the value of a secret after saving?
No, GitHub encrypts secrets and never displays them again. If you need to verify or change a secret, you must update it with the new value. There's no way to retrieve the original.
Are secrets visible in workflow logs?
GitHub automatically masks secret values in logs, replacing them with ***. However, if your code transforms the secret (base64 encode, split, etc.), the transformed value might not be masked.
What's the difference between secrets and variables?
Secrets are encrypted and hidden, used for sensitive data like API keys. Variables are plain text and visible, used for non-sensitive configuration like feature flags or environment names.
Related guides:How to Hide API Keys · How to Enable Secret Scanning · Environment Variables Guide