Environments, Deployments, Review Apps, Protected Environments, and Deployment Approvals: Configuration, Design Choices, and Tradeoffs
Turn environment mechanics into deliberate architecture for static versus dynamic targets, deployment governance, scoped secrets, and reproducible rollback.
Learning objectives
- Choose static versus dynamic environments from lifecycle and ownership needs.
- Choose automated deployment, manual execution, or paid approval governance without conflating them.
- Design environment-scoped variable boundaries and explain their limits.
- Prefer rollback by known immutable artifact when reproducibility matters.
- Justify deployment choices across security, reliability, maintainability, compatibility, performance, and cost.
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. Start with the external system, then model it in GitLab
The safest deployment architecture does not start by inventing environment names in YAML. First identify the real deployment targets and their invariants: whether they are shared or per-change, how they are addressed, who owns them, what credential writes to them, what constitutes successful health, and how they are removed or restored. Then make GitLab environment/deployment records reflect those facts.
A mismatch between external topology and GitLab naming is
operational debt. For example, calling ten review environments
review/* while all jobs overwrite one shared server
creates false concurrency and false isolation.
2. Static environment versus Review App
| Dimension | Static environment | Dynamic Review App |
|---|---|---|
| Identity | Stable name such as staging. |
Name includes change/ref identity such as
review/$CI_COMMIT_REF_SLUG.
|
| Lifetime | Long-lived; many deployments update one target. | Short-lived; tied to branch/MR/change lifecycle. |
| Best use | Shared integration, staging, production. | Visual/functional review of isolated changes. |
| Cleanup | Usually no teardown after each deployment. | Explicit stop + TTL/auto-stop strongly recommended. |
| Cost shape | Stable baseline infrastructure. | Potentially high fan-out if many changes create resources. |
| Trust risk | High if broadly shared credential/target. | High if untrusted change code can create networked resources or receive secrets. |
Review Apps improve feedback only if external isolation is real. If per-MR infrastructure is expensive or unsafe, use a static test target with controlled scheduling rather than pretending to have dynamic isolation.
3. Automated deployment versus manual execution versus deployment approval
These answer different questions:
| Mechanism | Primary purpose | Key weakness |
|---|---|---|
| Automatic deployment job | Ship when pipeline conditions are satisfied. | Fast but dangerous if policy/credentials are too broad. |
when: manual |
Require a person to start a job. | Button visibility/role is not a complete approval/audit model. |
| Protected environment | Restrict allowed deployers (Premium/Ultimate). | Does not prove code was reviewed or target IAM is correct. |
| Deployment approval | Require configured approver(s) for a protected environment (Premium/Ultimate). | Paid feature; approval does not automatically execute the job. |
A mature production gate may combine MR governance, protected branch, pipeline checks, protected environment, deployment approvals, short-lived target identity, and an explicit operator run. Avoid adding a manual button solely to make a pipeline “feel controlled.”
4. Merge approval and deployment approval are intentionally separate
A merge approval answers “is this code change acceptable to integrate?” A deployment approval answers “is this particular deployment to this protected environment acceptable now?” The same code may be approved for merge but intentionally not deployed during an incident, freeze, or readiness problem. Conversely, redeploying a previously reviewed artifact may need deployment authorization without another source-code review.
5. Environment-scoped variable versus broad project variable
If a variable is needed only by production, scope it to
production rather than *. If a synthetic
review variable is needed by every Review App, scope it to
review/*. This reduces accidental exposure to unrelated
jobs.
| Variable design | Exposure | Use when |
|---|---|---|
* project variable |
Every matching project job context can potentially receive it. | Non-sensitive configuration truly needed everywhere. |
review/* scope |
Jobs attached to review environments. | Review-specific synthetic endpoint/config. |
production scope |
Production environment jobs. | Production-only credential/config, together with ref/runner/IAM controls. |
| External identity/secret manager | Retrieved at job time under identity/policy. | Prefer for real long-lived/high-value credentials when available. |
Scope is not a substitute for trust. If a production deployment job executes attacker-controlled code, that code can still access variables granted to the job.
6. Roll back by known artifact versus rebuild from source
There are two very different actions often called rollback:
- Redeploy known artifact: select the exact binary/image/package digest that was previously successful and deploy it again. This preserves identity.
- Rebuild old source: check out the old commit and run today’s build with today’s registries, base images, timestamps, dependency mirrors, and toolchain. This may produce different bytes.
GitLab’s deployment rollback reruns the deployment job for a previous deployment/commit; it does not automatically rerun upstream build jobs. Design the deploy job so it can retrieve/verify the previously identified artifact if deterministic rollback matters.
7. Deployment input contract
A production deploy job should have an explicit contract such as:
deployment input:
commit_sha: 7f3...c21
artifact_type: container-image
immutable_identity: registry.example/app@sha256:abc...
build_pipeline_id: 9012
deploy_pipeline_id: 9055
environment: production
target_id: cluster/prod-eu1
verification: health-check + version endpoint
This is more useful than “deployed main.” It lets incident responders reproduce the exact intended state.
8. Explicit stop policy versus implicit cleanup
Current GitLab can automatically stop environments when their
associated branch is merged/deleted, even without an explicit
on_stop job, but the documentation notes a proposed
change for production/staging behavior. Treat implicit stop as
convenience, not your external cleanup contract. Define explicit
stop actions for resources that actually need teardown, and verify
the external resource afterward.
9. auto_stop_in: safety net versus state surprise
A short TTL limits leaked review resources, but every new deployment resets the timer and periodic background processing means exact timing is approximate. A very short TTL can surprise reviewers; a very long TTL hides cleanup defects. Choose a duration from expected review lifetime and cost, then monitor stale external resources independently.
10. Deployment history as an audit/recovery index
GitLab preserves deployment metadata and keeps deployed commits reachable even as old deployment refs may be archived/cleaned. Use environment history to identify the last known-good deployment, but preserve artifact/package/image retention separately. A commit being reachable does not mean its original build artifact still exists.
11. Tier and offering matrix
| Capability | Free | Premium/Ultimate | Offering notes |
|---|---|---|---|
| Environments/deployments | Yes | Yes | GitLab.com, Self-Managed, Dedicated. |
| Review Apps/dynamic environments | Yes | Yes | External infrastructure cost is separate. |
on_stop/auto_stop_in |
Yes | Yes | Background stop timing is approximate. |
| Project variable environment scope | Yes | Yes |
Do not use as pipeline-creation input in
rules/include.
|
| Protected environments | No | Yes | GitLab.com, Self-Managed, Dedicated. |
| Deployment approvals | No | Yes | Require protected environments. |
| Group variable environment scope | No | Yes | Group-level scoping is paid. |
12. Worked architecture decision
A team has 20 daily MRs, one shared staging environment, and one production target. They want low cost and strong production governance.
| Decision | Choice | Reasoning |
|---|---|---|
| Review experience | Synthetic/local Review App for docs/UI changes; shared ephemeral test for backend integration. | Avoids 20 expensive full-stack environments while retaining change-level feedback where useful. |
| Staging |
Static staging + resource_group.
|
One target, so serialization matches reality. |
| Production |
Static production, known artifact digest,
target health verification.
|
Preserves audit/recovery identity. |
| Secrets | Project variables scoped by environment or external short-lived identity. | Reduces broad exposure. |
| Paid governance if licensed | Protected production environment + deployment approval. | Separates deploy authorization from merge review. |
| Free fallback | Protected branch/MR governance + manual production job + least-privilege external IAM and documented human process. | Teaches same operating model without requiring purchase. |
13. Evaluate tradeoffs across six dimensions
- Maintainability: can teams predict environment names/lifecycles without custom cleanup scripts everywhere?
- Security: can untrusted code reach deployment credentials, protected runners, or target networks?
- Reliability: can deployments be serialized where the target cannot accept concurrency, and can stale deployments be prevented?
- Compatibility: do Self-Managed version and runner/executor behavior support the chosen syntax/features?
- Performance: do Review Apps improve feedback enough to justify runner/deployment latency?
- Cost: how many dynamic resources, pipelines, artifacts, and hosted-runner minutes can exist concurrently?
14. Production policy template
Environment policy
- environment names map 1:1 to documented target classes
- every deploy logs commit + immutable artifact identity + target identifier
- review/* has explicit stop action and bounded TTL
- production deploys are serialized and verified externally
- production credential is environment-scoped / short-lived
- rollback redeploys known artifact identity; rebuild is a separate recovery mode
- protected-environment/approval rules are documented if licensed
- exception path names approver, reason, expiry, evidence, and follow-up
Knowledge check
When is a static environment preferable to a Review App?
When many changes intentionally deploy to one shared target and per-change isolation is not real or worth the cost.
Why is when: manual not equivalent to deployment
approval?
It requires someone to start a job but does not implement the protected-environment approval model or necessarily separation of duties.
What does environment-scoped variable access reduce?
Exposure of the value to unrelated environment jobs; it does not make code inside the matching job trustworthy.
Which rollback strategy best preserves identity?
Redeploy the exact previously known artifact/image/package digest rather than rebuilding old source.
Why define explicit stop actions even though GitLab may auto-stop branch environments?
External cleanup must remain explicit and verifiable, and implicit platform behavior can change or fail to remove external resources.
Summary
Environment design is topology and trust design. Static versus dynamic names should match real external isolation; automation, manual runs, and deployment approvals solve different governance problems; scoped variables reduce exposure; and known-artifact rollback preserves identity. A production environment record becomes useful when every deployment can be independently tied to an immutable input and verified target.
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.