GitHub Actions: Secrets and Env

GitHub Actions: Secrets and Env
"A secret is only a secret if it's never seen. In DevOps, a leaked secret is a leaked company."
The most dangerous thing a developer can do is type a password into a code file. If you git push a password, it is saved in history FOREVER. Even if you delete it $1$ second later, a hacker can find it in the "Git History."
In professional $2026$ engineering, we use Encrypted Secrets. The password is encrypted by GitHub and never shown to anyone—not even the CEO. This 1,500+ word guide is your architectural manual for Secrets Management, Environment Variables, and the "Secret-less" future of OIDC.
1. Secrets vs. Variables: The Difference
- Secrets: Encrypted. Use for: DB Passwords, API Keys, SSH Private Keys. (GitHub shows them as
***in the logs). - Variables: Plain Text. Use for: App Names, Port Numbers, Team Emails. (GitHub shows these clearly in the logs).
2. Using Secrets in YAML: The ${{ }} Syntax
- name: Login to API
run: my_tool --api-key ${{ secrets.MY_VERY_SECRET_KEY }}Architecture Note: GitHub automatically "Redacts" these in the terminal. If your tool accidentally prints the password to the screen, GitHub will intercept it and replace it with *** to save you from a hack.
3. Repository vs. Org Secrets
In a large company, you might have $100$ repos that all use the same "Slack Token."
- You don't want to copy-paste that token $100$ times.
- The Solution: Organization Secrets.
- You save it ONCE in the Org Settings.
- You then select which repositories are allowed to "See" it. This is the "Single Source of Truth" principle.
4. OIDC (OpenID Connect): The No-Key Future
The #1 goal of 2026 is to have Zero Secrets in GitHub. How? OIDC.
- Your GitHub Action "Identifies" itself to AWS/Azure using a temporary token.
- AWS trusts GitHub. It gives you a $5$-minute "Key" that vanishes after the job is done.
- The Benefit: There is NO static password saved in GitHub. If your GitHub is hacked, the hacker finds Nothing.
5. Security: The "Step-Output" Leak
A common mistake: A developer tries to be "Helpful" and outputs a secret from one step to another.
- name: Extract Key
id: step1
run: echo "key=${{ secrets.PK }}" >> $GITHUB_OUTPUTThe Warning: While GitHub redacts the secret in the "Run" step, it might NOT redact it in the "Output" visualization or third-party logging tools.
- The Rule: Never pass secrets through
GITHUB_OUTPUT. Pass them through Environment Variables (env:) instead.
Summary: The Secrets Checklist
- Never Hardcode: Search your repo for
"or'followed bykeyorpass. - Org over Repo: Use Organization secrets for shared tokens to reduce maintenance.
- Smallest Scope: Only give a secret to the Job that actually needs it. Don't put all secrets at the top of the file.
- Use OIDC: Move to "Secret-less" architecture for AWS/GCP/Azure deployments.
- Audit Logs: Check your audit logs once a week to see if any new secrets were added by unauthorized users.
Secrets and Variables are the "Access Card" of your infrastructure. By mastering the encryption of keys and the discipline of the "Secret-less" OIDC bridge, you gain the ability to build and deploy systems that are virtually un-hackable. You graduate from "Managing passwords" to "Architecting Trust."
Frequently Asked Questions
Q: What is the difference between environment variables and secrets in GitHub Actions?
Environment variables store non-sensitive configuration — base URLs, feature flags, log levels — and are visible in workflow logs. Secrets store sensitive values — API keys, tokens, passwords — and are masked in logs; GitHub never exposes their raw value in output. Set variables with env: at workflow, job, or step level. Set secrets in repository Settings → Secrets and reference them as ${{ secrets.SECRET_NAME }}.
Q: How do you pass a secret into a shell command safely in GitHub Actions?
Reference the secret as an environment variable in the step, then use the variable name in the command — never interpolate ${{ secrets.X }} directly into a run: script, as that can expose the value in error messages. Correct pattern: env: { MY_TOKEN: "${{ secrets.API_TOKEN }}" } then in the script: curl -H "Authorization: Bearer $MY_TOKEN" .... GitHub also auto-masks known secret values in logs as an extra safety net.
Q: What are GitHub Actions environment variables that are always available?
GitHub provides built-in variables like GITHUB_SHA (commit hash), GITHUB_REF (branch/tag ref), GITHUB_REPOSITORY (owner/repo), GITHUB_ACTOR (user who triggered the run), GITHUB_WORKSPACE (checkout path), and GITHUB_RUN_ID (unique run identifier). Access them as $GITHUB_SHA in shell or ${{ github.sha }} in expression syntax. The full list is in the GitHub Actions documentation under "Default environment variables."
Part of the GitHub Mastery Course — engineering the security.
