Environments, Deployments, Review Apps, Protected Environments, and Deployment Approvals: Concepts, Architecture, and Mental Model
Model GitLab environments as auditable deployment records around external targets, with explicit artifact identity, lifecycle, trust, protection, and recovery boundaries.
Learning objectives
- Separate a GitLab environment record from the external infrastructure it describes.
- Relate deployment records to jobs, commits, refs, pipelines, environment state, and external URLs.
- Explain dynamic environments and Review Apps without assuming Kubernetes or cloud infrastructure.
- Reason about stop actions, automatic stop, environment-scoped variables, rollback/redeploy, protection, and approvals.
- Inspect environment/deployment state before changing CI/CD configuration.
environment:on_stop,
environment:auto_stop_in, environment/deployment APIs,
deployment rollback/redeploy, and project-level environment-scoped
CI/CD variables are available on GitLab Free/Premium/Ultimate across
GitLab.com, Self-Managed, and Dedicated. Protected environments and
deployment approvals are Premium/Ultimate. Group-variable environment
scoping is also Premium/Ultimate, so the mandatory chapter path uses
only a disposable project and synthetic project-level values. No cloud
account, Kubernetes cluster, production endpoint, real credential, or
paid approval feature is required.
1. The practical problem: “the pipeline is green” does not tell you what is deployed
Chapter 16 gave pipeline outputs an identity: producer job, pipeline, commit SHA, checksum, and retention. Deployment introduces another state transition. A job can succeed while publishing nothing, publish to the wrong endpoint, or update an external system while GitLab records a misleading environment name. Conversely, GitLab can show a healthy environment record even when the external target is down because the environment record is not the infrastructure itself.
The operational question is therefore not just “did deploy succeed?” It is: which immutable source/artifact identity did which job attempt to deploy, to which target, under which environment name, and what evidence proves the external state matches the GitLab record?
2. Mental model: control-plane record around an external target
flowchart TD C[Commit SHA] -- pipeline config --> P[Pipeline] A[Build artifact + digest] -- needs edge --> J[Deployment job] P -- schedules --> J J -- deployment record --> D[(GitLab deployment)] D -- belongs to --> E[(GitLab environment)] J -- external action --> T[External target or simulation] E -- URL / metadata --> T V[Scoped variable / credential policy] -- job-time access --> J G[Protection / approval policy] -. optional paid gate .-> J
The commit and artifact identify what you intend to ship. The deployment job performs or simulates the external action. GitLab then records a deployment associated with an environment. The environment contains GitLab metadata such as name, URL, tier, state, and deployment history; it does not continuously prove the target matches that metadata. Variables, protected-environment rules, and approval policies govern who/what can act at the boundary.
3. Define the resources before using the UI
| Term | What it is | What it is not | Identity question |
|---|---|---|---|
| Environment | A GitLab record representing a deployment target such as development, staging, production, or a dynamic review environment. | Not the VM, cluster, server, namespace, or cloud account itself. | Which logical target does GitLab believe this deployment belongs to? |
| Deployment | A GitLab record created by a deployment job and associated with an environment, commit/ref, job, status, and timestamps. | Not merely a Git commit and not the artifact payload. | Which job/commit was recorded as deployed and with what result? |
| Review App | A dynamic environment used to review a change, commonly one per branch/MR, with a discoverable URL. | Not necessarily Kubernetes and not automatically a real endpoint. | Which change owns this temporary environment and when should it stop? |
| Protected environment | A Premium/Ultimate GitLab policy limiting who may deploy to a sensitive environment. | Not a branch protection rule and not network authorization. | Which actors are allowed to deploy? |
| Deployment approval | A Premium/Ultimate approval gate on a protected environment. | Not merge-request approval. | Who must authorize this deployment instance? |
4. A deployment record is hosted metadata around a job
A job becomes a deployment job when it declares an
environment. A successful job normally creates a
successful deployment record; failed/canceled deployment attempts
are also visible in history. The deployment points back to the job
and commit, which lets operators correlate pipeline evidence with
environment history.
deploy_development:
stage: deploy
script:
- test -f out/product.txt
- sha256sum -c out/product.txt.sha256
- echo "Simulated deployment only; no external system changed"
environment:
name: development
url: https://development.example.invalid
deployment_tier: development
The URL is metadata and a navigation aid. GitLab does not verify
that development.example.invalid exists, nor that the
job actually wrote to that target. In this course we deliberately
use the reserved .invalid domain to prove the
separation between GitLab’s record and external infrastructure.
5. Read-only inspection first
Before creating or changing an environment, inspect the project’s existing environment and deployment inventory. A UI path is Operate → Environments. The REST APIs provide machine-readable evidence without requiring a mutation:
PROJECT_ID="12345678"
glab api "projects/$PROJECT_ID/environments" --paginate \
--jq '.[] | {id,name,state,external_url,tier,updated_at}'
glab api "projects/$PROJECT_ID/deployments?order_by=finished_at&sort=desc" --paginate \
--jq '.[] | {id,status,ref,sha,environment:.environment.name,deployable_job_id:.deployable.id,finished_at}'
Use synthetic numeric IDs in course notes. Do not paste private API
tokens into commands; glab should use its authenticated
profile. If the API output contains private URLs or project
information, capture only the fields needed for the lab.
6. Static and dynamic environments
A static environment has a stable name such as
staging. Many deployments over time update the same
logical target. A dynamic environment embeds change
identity in the environment name, such as
review/feature-login. GitLab groups dynamic
environments by prefix, which makes Review Apps easier to manage.
review_app:
stage: deploy
script:
- echo "Create a synthetic review deployment record"
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_ENVIRONMENT_SLUG.example.invalid
deployment_tier: development
Dynamic naming is a metadata contract. If the external deploy script always writes to one shared test server, five GitLab review-environment names still do not create five isolated targets. The script/infrastructure must implement the isolation the names claim.
7. Review Apps connect change lifecycle to environment lifecycle
A Review App is usually created from an MR/branch pipeline so reviewers can inspect a running version of the proposed change. GitLab supplies the environment record and can surface the URL in merge-request workflows; your deployment code still creates the actual service. Review Apps are available on Free, but a real Review App may consume cloud, cluster, DNS, or runner resources. The mandatory labs use a synthetic URL and no external system.
A useful Review App policy answers: which pipeline source creates it, which branch/MR identity names it, whether untrusted fork code can deploy, which variables it receives, how it is stopped, and what cleanup evidence proves its external resources are gone.
8. Stop actions: state transition plus external cleanup
Stopping an environment should mean two things: GitLab marks the
environment stopped, and your stop job tears down the external
resource. Declare the stop job with
environment:action: stop and connect it through
on_stop.
deploy_review:
stage: deploy
script: echo "synthetic deploy"
resource_group: review-$CI_COMMIT_REF_SLUG
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_ENVIRONMENT_SLUG.example.invalid
on_stop: stop_review
auto_stop_in: 1 day
stop_review:
stage: deploy
script: echo "synthetic teardown"
resource_group: review-$CI_COMMIT_REF_SLUG
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop
when: manual
Using the same resource_group for deploy and stop also
supports the current UI Stop behavior documented by GitLab. In
production, the stop script must be idempotent: “already gone”
should be a safe, observable result rather than a destructive error
cascade.
9. auto_stop_in is lifecycle policy, not a precise
scheduler
environment:auto_stop_in schedules an environment to be
stopped after a human-readable duration. GitLab’s background worker
checks expirations periodically, so the stop is not guaranteed at
the exact second the interval elapses. Every new deployment resets
the environment’s lifetime. Current GitLab also supports resetting
or preserving the schedule with environment actions such as
access, prepare, and verify.
Do not use auto_stop_in as the only control for
expensive infrastructure. Combine it with idempotent external
cleanup and inventory/TTL controls in the actual platform.
10. Environment-scoped variables narrow credential exposure
A project CI/CD variable can be scoped to an environment name or
wildcard such as review/* or production.
This is valuable because a job only receives the variable when its
environment matches the scope. It reduces exposure but does not make
untrusted deployment code safe: code that receives the variable can
still use it.
rules or
include, because those values may not be available when
the pipeline configuration is validated. Use them for job-time
deployment access, not to decide whether the pipeline exists.
11. Rollback means a new deployment pointing to an older known state
GitLab can retry/redeploy a deployment or roll back to a previously successful deployment. A rollback creates a new deployment record and a new job ID pointing to the commit being restored. Only the deployment job is rerun; upstream build jobs are not magically rerun. This is why the safest rollback architecture deploys an immutable, retained artifact by digest/identity instead of rebuilding mutable dependencies.
If your deploy job rebuilds from source, “rollback to commit X” may produce bytes different from the original deployment of X. Preserve artifact identity separately when production reproducibility matters.
12. Protected environments and deployment approvals are separate paid governance layers
Protected environments are Premium/Ultimate and restrict who may deploy to an environment. Deployment approvals are also Premium/Ultimate and add required approvers to protected-environment deployments. They do not replace branch protection, runner isolation, secret scope, or external cloud IAM.
| Control | Question answered | Free mandatory path |
|---|---|---|
| Protected environment | Who may deploy to this GitLab environment? | Simulate with a policy fixture and use a disposable unprotected environment. secure real infrastructure separately. |
| Deployment approval | Who must approve this deployment instance before it can proceed? | Use a fixture/read-only walkthrough; do not purchase a tier for this chapter. |
| MR approval | Who approved the code change? | Already covered in Chapters 7–8; distinct from deployment approval. |
| Cloud/Kubernetes IAM | What can the job credential do in the actual target? | No real cloud target in mandatory labs; design least privilege separately. |
Current behavior also matters operationally: a deployment approval does not automatically start the corresponding deployment job. After all required approvals are satisfied, the job still must be run according to GitLab’s deployment-approval workflow. By default, the pipeline triggerer cannot self-approve unless that option is explicitly enabled.
13. Trust boundary: environment metadata does not constrain a runner
A job named deploy_production can run on an
over-privileged runner and reach unrelated systems. Conversely, a
protected environment can prevent GitLab from authorizing a
deployment job but cannot revoke a credential that someone copied
elsewhere. Treat these as composing controls: reviewed config +
protected refs + scoped variables/short-lived identity + isolated
runners + target IAM + environment protection/approval + deployment
evidence.
14. DevOps connection: deployment state must be falsifiable
A useful environment record lets another engineer independently falsify or confirm the claim “commit/artifact A is deployed to target B.” Record commit SHA, artifact digest, deployment job and pipeline IDs, environment name, external target identifier, actor/policy evidence, and a post-deploy verification signal. Without that chain, environment dashboards become labels rather than operational evidence.
Knowledge check
Does a GitLab environment prove the external target actually matches its URL/name?
No. It is GitLab metadata. The deployment script and independent verification must prove the external state.
What new record does a deployment rollback create?
A new deployment with its own job ID that points to the commit being rolled back to.
Why is auto_stop_in insufficient as the only
cloud-cost control?
Stopping is checked periodically and GitLab environment state alone cannot guarantee external resources were destroyed.
Are deployment approvals the same as merge-request approvals?
No. MR approvals govern code integration; deployment approvals govern a deployment to a protected environment.
Why should rollback prefer a known immutable artifact?
Rebuilding an old commit against mutable dependencies can produce different bytes and break reproducibility.
What Free-compatible control narrows project variable availability to review environments?
A project CI/CD variable with an environment scope such as
review/*.
Summary
GitLab environments are auditable records around deployment targets;
deployments connect jobs and commits to those records. Review Apps
add change-scoped dynamic environments, stop actions and
auto_stop_in manage lifecycle, scoped variables narrow
runtime exposure, and rollback should preserve known artifact
identity. Protected environments and deployment approvals add paid
governance, but they compose with—not replace—runner, ref, secret,
and external-IAM controls.
Official references
- GitLab Docs — Environments
- GitLab Docs — Deployments
- GitLab Docs — Review apps
- GitLab Docs — Deployment safety
- GitLab Docs — Protected environments
- GitLab Docs — Deployment approvals
- GitLab Docs — Environments API
- GitLab Docs — Deployments API
- GitLab Docs — Protected environments API
- GitLab Docs — CI/CD variables
- GitLab Docs — CI/CD YAML syntax reference
- GitLab Docs — Predefined CI/CD variables
- GitLab Docs — Where variables can be used
- GitLab Docs — Resource groups
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.