Topic 2.2
Secrets & Environments
In one line
GitHub Actions Secrets keep real credentials out of your workflow YAML entirely, and Environments add approval gates and environment-specific secrets on top — the same never-commit-a-real-credential lesson, applied to pipeline configuration.
Key ideas
- 01
This directly extends the exact lesson Git's own course covered in depth (Phase 7.5) and Kubernetes' own course revisited (Phase 5.3) — a real credential (a deployment key, a database password, a cloud API token) must NEVER be written directly into a workflow YAML file, since that file is committed to Git and subject to the exact same permanent-history exposure risk as any other committed secret.
- 02
GITHUB ACTIONS SECRETS (configured in a repository's or organization's own Settings, never in a YAML file) are referenced in a workflow via
\${{ secrets.MY_SECRET }}— GitHub injects the actual value at runtime, automatically MASKS it in any log output (replacing it with***if it would otherwise be printed), and the value itself is never visible in the workflow file or its history at all. - 03
ORGANIZATION-level secrets can be shared across MANY repositories at once, while REPOSITORY-level secrets are scoped to just one — genuinely useful for a shared credential (an internal artifact registry's access token, say) that many repositories legitimately need, versus one that's genuinely specific to a single project.
- 04
An ENVIRONMENT (
production,staging) is a named, configurable target that a job can be scoped to (environment: production) — environments can have their OWN secrets (a production database URL genuinely different from staging's), and critically, can require MANUAL APPROVAL from a specific person or team before a job targeting that environment is allowed to actually run. - 05
This required-approval mechanism is genuinely the same 'deployment approvals' concept the original DevOps roadmap this course draws from names directly (Section 26, CI/CD Security) — a deployment to
productioncan be configured to pause and wait for an explicit human click before proceeding, giving a real, deliberate gate on the single most consequential kind of pipeline action, even within an otherwise fully automated CI/CD setup. - 06
Secrets are correctly scoped to the NARROWEST level that genuinely needs them — a production deployment credential belongs in the
productionenvironment's own secrets, never as a repository-wide secret every job (including unrelated ones that don't deploy anywhere) could potentially access, exactly the same least-privilege principle Linux, Docker, and Kubernetes' own courses have each applied in their own specific context.
Code & diagrams
A production deployment specifically requires human approval before it's allowed to proceed.
A secret referenced safely, and a production environment requiring approval.
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mvn test
deploy-staging:
needs: test
runs-on: ubuntu-latest
environment: staging # uses staging's own scoped secrets
steps:
- run: |
echo "Deploying to staging..."
curl -H "Authorization: Bearer ${{ secrets.STAGING_DEPLOY_TOKEN }}" \
https://staging.example.com/deploy
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production # this one has a required reviewer configured
steps:
- run: |
echo "Deploying to production..."
curl -H "Authorization: Bearer ${{ secrets.PROD_DEPLOY_TOKEN }}" \
https://example.com/deployExplain it without notes
Why does referencing a secret as \${{ secrets.MY_SECRET }} in a workflow file, committed to Git, not actually expose the secret's real value the way hardcoding it directly would?
Why would a team specifically configure required-reviewer approval on the production environment but not on staging?
Practice
Add a secret to a real GitHub repository's settings and reference it in a workflow, confirming with a deliberate echo (or checking the logs) that GitHub correctly masks it rather than printing the real value.
Configure a GitHub Environment with a required reviewer, and confirm a job targeting that environment genuinely pauses and waits for manual approval before proceeding.
Trade-offs
- ↔
Requiring manual approval on production deployments is a genuine, deliberate trade-off between speed and safety — it means production deployments can never be fully 'Continuous Deployment' (Topic 0.1) in the strictest sense, since a human step is always in the path, but for the vast majority of real teams, that trade is exactly correct: the cost of a brief manual approval pause is small compared to the cost of an unreviewed, fully automatic production deployment going wrong.
Done when you can
I can add and reference a GitHub Actions secret without ever writing its real value into a workflow file.
I understand how a GitHub Environment scopes secrets and can require manual approval.
I know to scope a secret to the narrowest environment that genuinely needs it, not repository-wide by default.