Environments, Deployment Protection Rules, Approvals, Concurrency, and Rollbacks: Concepts, Architecture, and Mental Model
Chapter 18 turned automation into reusable platform code. Chapter 19 asks the next operational question: after CI has produced evidence and an identified artifact, how does that exact change cross a boundary such as staging or production without racing another deployment, leaking environment credentials, or turning rollback into an untraceable emergency rebuild?
Learning objectives
- Distinguish an environment object, an Actions job that references it, a deployment record, and deployment status evidence.
- Explain required reviewers, wait timers, deployment branch/tag restrictions, custom protection rules, and environment-scoped secrets/variables.
- Reason about concurrency groups, pending/running states, cancellation versus queueing, and why environment names and concurrency names are independent.
- Define promotion as moving the same identified artifact through environments and rollback as a new controlled deployment of a known-good input.
- Trace trust boundaries among source commit, workflow revision, artifact digest, approver, runner, target environment, and external deployment system.
Availability: The mandatory model targets GitHub.com. Public repositories can use environments on current plans; on GitHub Free, required reviewers, wait timers, environment secrets/variables, and related protection controls used here are available on public repositories. Private/internal availability differs by plan. Custom deployment protection rules are currently public preview and are optional in this chapter.
1. Deployment is a governed state transition, not “run the last shell command”
CI proves facts about a revision: tests passed for commit
S; artifact A was produced; digest
D identifies its bytes. Deployment changes another
system. The moment a workflow can alter a staging or production
target, additional questions appear: which environment is being
changed, who approved that exact input, which run currently owns the
target, and what evidence proves the resulting state?
GitHub environments model part of that boundary. They do not deploy software by themselves. Instead, a workflow job references an environment, GitHub evaluates the environment's rules, and—by default—GitHub records a deployment associated with that job. Your deployment command, cloud API, package promotion, or configuration update remains a separate operation executed after the gate.
2. Separate four hosted objects before reasoning about protection
| Object | What it represents | Evidence to inspect |
|---|---|---|
| Environment |
A named GitHub repository resource such as
staging or production with
protection rules, variables, and secrets.
|
Settings → Environments;
GET /repos/{owner}/{repo}/environments/{name}.
|
| Workflow job |
Execution unit whose environment: key requests
access to one environment.
|
Workflow graph, job logs, event/ref/SHA, workflow file revision. |
| Deployment | GitHub record for a request to deploy a ref/SHA to an environment; Actions creates one by default when a job references an environment. | Repository deployment history or Deployments REST API. |
| Deployment status | State attached to a deployment, such as queued/in_progress/success/failure/error. | Deployment history/status API and linked workflow logs. |
An environment can exist with no active deployment. A deployment can fail while the environment still exists. Deleting an environment deletes its secrets and protection rules; it does not rewrite Git history. These are different lifecycles.
3. The deployment trust flow
flowchart TD
C["Commit SHA S"] --> B["Build and verify"]
B --> A["Artifact bytes + digest D"]
A --> ST["Staging environment gate"]
ST --> P["Production environment gate"]
P --> T["Target state"]
R["Reviewer / protection rule"] --> P
K["Concurrency group"] --> P
T --> E["Deployment status + logs"]
E --> RB["Rollback decision"]
RB --> A2["Known-good artifact D0"]
A2 --> P
The arrows are contracts, not decoration. Commit → build establishes source identity. Build → artifact freezes the bytes that were tested. Artifact → staging → production is promotion of the same payload rather than a second build. Reviewer/protection rule → production authorizes that specific transition. Concurrency → production prevents overlapping writers. Target → evidence records what happened. Rollback → known-good artifact starts a new governed deployment instead of mutating history.
4. Protection rules gate the job before sensitive execution
Required reviewers create a manual decision point. Up to six users
or teams can be listed and one approval is sufficient; reviewers
need at least read access. Prevent self-review is the
separation-of-duties control that stops the triggering actor from
approving the same deployment. A wait timer delays execution without
consuming billable runner time. Deployment branch/tag restrictions
constrain which GITHUB_REF values may target the
environment. Custom deployment protection rules use GitHub Apps to
obtain an external approval/rejection and are currently public
preview.
Environment secrets are withheld until protection rules pass.
Environment variables are exposed through the
vars context only to jobs that reference the
environment. This makes the environment a useful authorization
boundary, but not a sandbox: code that runs after approval still has
whatever network reachability and runner trust the job provides.
5. Concurrency controls writers; environment rules control admission
An environment named production and a concurrency group
named production are not automatically connected. The
environment gate decides whether a job may proceed. The concurrency
group decides how jobs/runs sharing a key compete for execution. If
another workflow targets the same environment without the same
concurrency discipline, that other workflow is not serialized merely
because the environment name matches.
jobs:
deploy:
environment: production
concurrency:
group: deploy-production
queue: max
runs-on: ubuntu-latest
steps:
- run: echo "one production writer at a time"
Current GitHub.com supports queueing multiple pending members with
queue: max. The default queue mode keeps one pending
member and replaces an older pending one.
cancel-in-progress: true additionally cancels the
running member. That is often desirable for preview/staging
validation of stale commits, but dangerous for a production
migration that must finish once started. Concurrency group names are
case-insensitive and should be namespace-specific enough that
unrelated workflows cannot cancel one another.
6. Approval must bind to immutable input identity
“Approve release 2.4” is ambiguous if the underlying tag can move or if the artifact can be rebuilt. The approval packet should state at least the source commit SHA, workflow run ID, artifact name, a digest of the deployable bytes, intended environment, and any change/ticket reference. The deployment job should independently verify that the downloaded bytes still match the approved digest before executing a target mutation.
GitHub environment approval gates a workflow job; it does not automatically prove artifact provenance. That evidence comes from the build process, hashes/attestations, and your promotion policy. Chapter 25 will go deeper on attestations; here the minimum invariant is simpler: do not rebuild during promotion or rollback when the purpose is to redeploy already-verified bytes.
7. Rollback is a forward operation toward a known-good state
A rollback should answer the same questions as a normal deployment: which artifact, which digest, which approver, which environment, which run, and what target state resulted? Rebuilding old source during an incident weakens that evidence because compilers, package indexes, base images, or build tools may have changed. Prefer redeploying a retained known-good artifact or applying a separately reviewed compensating change.
Git history operations such as reverting a commit may be part of recovery, but a Git revert is not itself a runtime rollback. Conversely, redeploying an older artifact does not rewrite the repository. Keep source history and deployment state conceptually separate.
8. Read hosted state before writing policy
Use explicit repository context. A missing environment and an inaccessible environment are different diagnoses.
REPO="OWNER/c19-deployment-governance-lab"
gh repo view "$REPO" --json nameWithOwner,visibility,defaultBranchRef --jq '{repo:.nameWithOwner, visibility, default_branch:.defaultBranchRef.name}'
gh api -H "X-GitHub-Api-Version: 2026-03-10" "repos/$REPO/environments" --jq '{total:.total_count, names:[.environments[].name]}'
gh api -H "X-GitHub-Api-Version: 2026-03-10" "repos/$REPO/deployments?per_page=10" --jq '[.[] | {id,environment,ref,sha,created_at}]'
For public repositories, listing environments is read-accessible; configuring them requires repository-owner/admin authority. Treat this inspection as preflight evidence in change records.
9. Product boundaries
| Layer | Owns | Does not automatically guarantee |
|---|---|---|
| Core Git | Commits, branches, tags, local history. | That any runtime target changed. |
| GitHub Actions | Workflow/job execution, runner context, logs, token permissions, concurrency. | That a cloud target is healthy after a command returns. |
| GitHub environment/deployment | Protection gates, scoped vars/secrets, deployment records/statuses. | Artifact provenance or target-system authorization unless you connect them. |
| External cloud/registry/runtime | Actual service, package, cluster, VM, database, traffic state. | GitHub policy compliance unless integrated and observed. |
Knowledge checks
Does naming both an environment and a concurrency group “production” automatically link their behavior?
No. Environment admission and concurrency serialization are independent controls; every relevant deployment workflow must implement the intended concurrency policy.
Why is rebuilding source during rollback weaker than redeploying a known-good artifact?
A rebuild can produce different bytes because dependencies, tools, base images, or package indexes may have changed. Redeploying a retained, verified digest preserves the artifact identity that was previously approved/tested.
When can an environment secret become available to a protected deployment job?
Only after the environment protection rules pass and the job is sent to a runner. The environment gate therefore precedes access to that environment secret.
What is the danger of a typo such as “prodution” in an environment name?
GitHub can create the referenced environment automatically; the new environment has no intended protection rules or secrets, so the job may bypass the governance assumed for “production.”
Is a Git revert the same thing as a deployment rollback?
No. A revert changes repository history by adding a new inverse commit. A runtime rollback changes deployed state, ideally through a new governed deployment of known-good bytes.
Summary
Deployment governance binds source identity → artifact identity → environment admission → serialized mutation → deployment evidence. Environments provide admission and scoped configuration; concurrency controls competing writers; deployment records/statuses make transitions observable; and rollback should be another controlled transition using known-good inputs.
Next: Lesson 2 builds these objects in a disposable public repository and observes staging, protected production, failure, and rollback state directly.
Further reading
- GitHub Docs — Deployments and environments
- GitHub Docs — Managing environments for deployment
- GitHub Docs — Reviewing deployments
- GitHub Docs — Deploying with GitHub Actions
- GitHub Docs — Control workflow/job concurrency
- GitHub REST — Deployment environments (2026-03-10)
- GitHub REST — Deployments
- GitHub REST — Deployment statuses
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.