Chapter 19Lesson 01~190 minutes

Environments, Required Reviewers, Protection Rules, and Deployment Gates: Core Concepts and Mental Model

Chapter 18 standardized how workflows are adopted and reused. Chapter 19 adds a different control plane: an environment such as staging or production represents a governed deployment target, not a branch alias. The chapter separates source revision, deployment authorization, credential availability, GitHub deployment records, and the target state that a deployment actually changes.

EnvironmentsDeployment gatesRequired reviewersSecretsDeployment records

Learning objectives

  • Explain the control chain from successful build to protected deployment and deployment status evidence.
  • Distinguish branch protection, environment protection, CI checks, and external target health.
  • Predict exactly when environment secrets and variables become available to a job.
  • Inspect environment rules, deployment records, concurrency groups, and target evidence before changing them.
  • Design a deployment gate that does not mistake approval for artifact integrity or rollout success.

1. The practical problem: a successful build is not deployment authorization

A workflow can produce a correct build for an exact commit and still be unauthorized to change a deployment target. Production delivery adds independent questions: which target is being requested, which refs may deploy, who or what must approve, when credentials may become available, whether another deployment is already changing the same target, and what evidence proves the external target reached the intended state.

An environment gives GitHub a named target boundary such as lab-staging, production, or eu-west-1. A branch such as main can be one input to authorization, but it is not the target itself. Treating main == production collapses source selection, authorization, credentials, concurrency, and deployment history into one name and hides the controls Chapter 19 needs to make explicit.

2. Mental model: build evidence enters a governed deployment boundary

The causal chain begins with a build that already identifies its event, ref, source SHA, workflow revision, runner context, and build evidence. A deployment job then requests one environment. GitHub evaluates that environment's protection rules before sending the job to a runner. Only after the applicable rules pass can the job run and use environment-scoped secrets and variables. By default, referencing the environment also creates a GitHub deployment object and status history. The job may then change an external system—but that external state is separate from GitHub's record and must be verified independently.

Environment authorization and deployment evidence
flowchart TD
  A[Successful build for exact SHA] --> B[Deployment job requests environment]
  B --> C[Branch/tag policy]
  C --> D[Wait timer / reviewers / custom rules]
  D --> E[Job becomes eligible for runner]
  E --> F[Environment secrets + vars available]
  F --> G[Serialized deployment operation]
  G --> H[GitHub deployment + status record]
  G --> I[External target state]
  H --> J[Evidence packet]
  I --> J

The two final arrows matter: GitHub may record a successful deployment job while the application is unhealthy later, and an external system may change even if a later workflow step is cancelled. Deployment records and target health are related evidence, not interchangeable evidence.

3. State inventory before changing an environment

State Examples to capture Why it matters
Event/source event name, ref, exact SHA, workflow revision Proves what source requested deployment
Environment name, URL, deployment true/false Identifies GitHub target boundary and history mode
Protection reviewers, self-review, timer, custom rules, branch/tag policy Explains why the job is waiting/allowed/denied
Credential/config environment secret names, vars names, availability timing Shows scope without exposing values
Job/runner job ID, conclusion, runner OS/image Separates gate decisions from execution runtime
Concurrency group value, cancel-in-progress result, queued/in-progress run IDs Explains serialization/cancellation
GitHub deployment deployment ID, status IDs/states, environment URL GitHub control-plane audit trail
External target release/artifact digest, provider resource state, health result Proves the actual deployment outcome

Keep these state domains separate during incident response. A failed branch-policy check is not a runner capacity problem. A reviewer rejection is not an artifact-integrity failure. A successful deployment status is not proof that an external service is healthy. The evidence-first workflow starts by locating the layer that actually denied or changed state.

4. Protection rules: who or what must allow the job to proceed?

Current GitHub environments can use required reviewers, a wait timer, branch/tag restrictions, and custom deployment protection rules. Required reviewers can name up to six users or teams; only one of the configured reviewers needs to approve a pending job. You can optionally prevent a deployment initiator from approving their own run. A wait timer accepts 1 to 43,200 minutes and its waiting time is not billed as runner time because the protected job has not started on a runner yet.

Custom deployment protection rules are implemented by GitHub Apps and can integrate observability, change management, quality, or other external decision systems. GitHub currently allows a maximum of six deployment protection rules enabled on one environment. Custom rules are an authorization gate, not a replacement for verifying the built artifact or the target after rollout.

5. Branch and tag restrictions are environment policy, not branch protection

Deployment branches and tags decide which GITHUB_REF values may deploy to an environment. The environment can allow all refs, only protected branches, or selected branch/tag patterns. Patterns for branches and tags are configured separately. This is different from branch protection or rulesets, which govern repository changes such as pushes, merges, reviews, and status checks.

A robust production design can use both: branch protection establishes how a source revision reaches main; environment rules establish whether that ref may target production. Neither control proves that the produced artifact has the expected digest or that a rollout succeeded.

6. Environment credentials are withheld until the gate passes

Environment secrets are available only to jobs that reference that environment. Organization and repository secrets are read when a workflow run is queued; environment secrets are read when the job referencing the environment starts. If approval or another environment rule blocks the job, it cannot access the environment secret while waiting.

jobs:
  deploy:
    runs-on: ubuntu-24.04
    environment: production
    env:
      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
    steps:
      - shell: bash
        run: |
          if [ -z "$DEPLOY_TOKEN" ]; then
            echo "credential_state=missing" >&2
            exit 1
          fi
          echo "credential_state=present"

The example prints only a Boolean-like presence state. It never prints the secret value. Environment variables are non-secret configuration and are accessed through the vars context, for example ${{ vars.DEPLOY_SLOT }}.

7. Referencing an environment normally creates deployment records

By default, when a workflow job references an environment, GitHub creates a deployment object for that environment and deployment status objects as the job progresses. If an environment.url is supplied, it is recorded as the deployment environment URL and can appear in the run visualization and deployment UI. Those records are GitHub-owned history tied to the deployment request—not the external service itself.

environment:
  name: lab-staging
  url: https://example.invalid/ch19/${{ github.run_id }}

Current workflow syntax also supports deployment: false. This keeps environment secrets/variables and still enforces wait timers and required reviewers, but suppresses the GitHub deployment object. Custom deployment protection rules require a deployment object and are therefore incompatible with deployment: false. Use this mode for CI/configuration cases intentionally—not as a way to hide real production deployments from history.

8. Authorization and serialization solve different problems

An environment gate answers “may this job deploy?” Concurrency answers “may another job change the same target at the same time?” A production-like target should usually have a target-specific concurrency group whose value cannot accidentally collide with unrelated environments.

concurrency:
  group: deploy-${{ github.repository }}-lab-staging
  cancel-in-progress: false

cancel-in-progress: false is deliberate for a state-changing operation: a newer deployment waits instead of cancelling a deployment that may already have committed side effects. Chapter 11's cancellation rule still applies—cancellation cannot undo an external write that already happened.

9. Current plan and visibility assumptions

Capability Public repo Private/internal note
Environments Available on current plans Requires Pro/Team/Enterprise as documented
Environment secrets Available on current plans Free requires public; private/internal requires eligible paid plan
Deployment branch/tag rules Available for public repositories Private/internal depends on eligible paid plan
Required reviewers / wait timer Free/Pro/Team public repositories supported On Free/Pro/Team these rules are public-only; Enterprise/private availability differs
Custom deployment protection rules Public repositories on current plans subject to limits Private/internal requires eligible plan/visibility

The mandatory labs in this chapter therefore use a learner-owned public disposable repository, which keeps the path free-compatible and allows a real wait-timer gate. A required-reviewer extension needs an eligible second user/team if you want to prove human approval rather than simulate it.

10. Read-only inspection first

# Repository and workflow revision
git rev-parse HEAD
git status --short

# Inspect the workflow before dispatch
grep -n -E 'environment:|concurrency:|permissions:' .github/workflows/ch19-environment-lab.yml

# Optional authenticated local inspection with GitHub CLI
gh api \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  repos/OWNER/REPO/environments/lab-staging

gh api \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  'repos/OWNER/REPO/deployments?environment=lab-staging&per_page=5'

The API examples are read-only. Use a learner-owned repository and your existing authenticated CLI session; do not paste credentials into the command line or lesson logs.

Operating invariant. A production deployment is accepted only when the exact source/build evidence is known, the intended environment rules authorize the job, target-specific concurrency is correct, credentials appear only after authorization, the GitHub deployment/status record matches the run, and the external target is independently verified.

11. Lesson summary

  • An environment is a governed deployment target, not a branch alias.
  • Protection rules gate a job before it reaches a runner.
  • Environment secrets become available only to the environment job after the applicable gate passes.
  • Environment reference normally creates deployment/status history; deployment: false is an explicit exception.
  • Concurrency serializes target mutation; approval does not serialize it automatically.
  • Deployment history and external target health must both appear in evidence.
Next lesson

Environments, Required Reviewers, Protection Rules, and Deployment Gates: Guided Hands-On Workflow

Continue with the next lesson to build on the current concepts, evidence, security boundaries, and operational practices.

Knowledge check

A workflow is approved for production. Does approval prove the artifact digest is correct?

When does an environment secret become available?

Why is main not the same concept as production?

What does deployment: false change?

Why use target-specific concurrency in addition to environment approval?

Official references and version notes

Version-sensitive GitHub Actions behavior in this lesson was rechecked on 2026-09-10. Re-verify current plan, repository visibility, API, environment, and protection-rule behavior before production rollout.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.