Checkpoint Lab — Environments, Deployments, Review Apps, Protected Environments, and Deployment Approvals
Prove the complete deployment-state chain with predictions, a synthetic environment, stop action, deliberate failure, known-state recovery, history, and cleanup.
Checkpoint objectives
- Predict environment, deployment, job, ref, and artifact state before executing the lab.
- Create a disposable development/review deployment record with an explicit stop action.
- Bind the deployment to a known artifact checksum and commit SHA.
- Cause one safe failed deployment and recover without rebuilding the artifact.
- Stop/delete disposable resources and verify GitLab history remains auditable.
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. Mission: prove deployment state without a real cloud account
Your checkpoint is an end-to-end delivery-state proof. You will produce one immutable synthetic artifact, record a dynamic GitLab deployment to a fake Review App URL, stop it, create one deliberately failed deployment attempt, then recover by deploying the same known artifact identity. No external service, domain, Kubernetes cluster, registry, or secret is required.
Production-model connection: Chapter 16 established trustworthy data flow. Chapter 17 adds where/when that identified output is deployed and who may govern that action. Chapter 18 will add manual/time-based release controls on top.
2. Preflight and required assumptions
| Item | Mandatory assumption |
|---|---|
| Tier/offering | GitLab Free on GitLab.com, Self-Managed, or Dedicated is sufficient. |
| Project/ref |
Disposable project or ch17/checkpoint branch.
|
| Role | Developer for normal pipeline; Maintainer only for optional project settings or deletion. |
| Runner | Tiny jobs if available; CI Lint + evidence fixture if compute is unavailable. |
| External target | None; example.invalid only. |
| Credentials | None. Do not create deploy tokens, cloud keys, or environment secrets. |
| Paid governance | Protected environments/deployment approvals are simulated/read-only only. |
3. Write predictions before you run
| Event | Expected GitLab state | Expected external state |
|---|---|---|
| Build succeeds | Artifact + checksum tied to build job/pipeline/SHA. | Nothing deployed. |
| Review deploy succeeds |
Environment review/<slug> becomes
available; successful deployment recorded.
|
Nothing changes; URL is reserved .invalid.
|
| Stop job succeeds | Environment becomes stopped. | Synthetic cleanup log only. |
| Broken deployment runs | Failed deployment attempt/job evidence appears. | Nothing changes. |
| Recovery deploy succeeds | New successful deployment record for same known artifact/commit identity. | Nothing changes. |
Also predict the maximum number of deployment jobs that can touch
this environment at once: one, because deploy/stop/recovery share a
resource_group.
4. Checkpoint pipeline
stages: [build, deploy]
build_release:
stage: build
script:
- mkdir -p out
- printf 'release_sha=%s\n' "$CI_COMMIT_SHA" > out/release.txt
- sha256sum out/release.txt > out/release.txt.sha256
artifacts:
name: "ch17-release-$CI_COMMIT_SHORT_SHA"
paths:
- out/release.txt
- out/release.txt.sha256
expire_in: 2 days
access: developer
deploy_review:
stage: deploy
needs:
- job: build_release
artifacts: true
resource_group: ch17-$CI_COMMIT_REF_SLUG
script:
- sha256sum -c out/release.txt.sha256
- echo "target=synthetic-review"
- echo "deployment_verified=true"
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_ENVIRONMENT_SLUG.example.invalid
deployment_tier: development
on_stop: stop_review
auto_stop_in: 1 day
stop_review:
stage: deploy
resource_group: ch17-$CI_COMMIT_REF_SLUG
script:
- echo "external_cleanup=synthetic-success"
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop
when: manual
# Add only for the deliberate failure step, then remove/repair.
deploy_broken:
stage: deploy
needs:
- job: build_release
artifacts: true
resource_group: ch17-$CI_COMMIT_REF_SLUG
script:
- sha256sum -c out/release.txt.sha256
- echo "ERROR: synthetic target rejected deployment" >&2
- exit 42
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_ENVIRONMENT_SLUG.example.invalid
when: manual
5. Validate and inspect merged configuration
Use CI Lint/Pipeline Editor. Confirm: all three deployment jobs
point to the same environment name; all resource-affecting jobs
share the same resource_group; no external commands or
credentials exist; and deploy_review/deploy_broken
consume the same build artifact instead of rebuilding.
6. Commit to a disposable branch
git switch -c ch17/checkpoint
git add .gitlab-ci.yml
git diff --cached --check
git diff --cached
git commit -m "ch17: environment deployment checkpoint"
CHECKPOINT_SHA="$(git rev-parse HEAD)"
printf 'checkpoint_sha=%s\n' "$CHECKPOINT_SHA"
git push -u origin ch17/checkpoint
7. Verify build and first deployment
After build_release and
deploy_review succeed, capture:
PROJECT_ID="12345678"
PIPELINE_ID="123456789"
ENV_NAME="review/ch17-checkpoint"
glab api "projects/$PROJECT_ID/pipelines/$PIPELINE_ID" \
--jq '{id,sha,ref,source,status}'
glab api "projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs" --paginate \
--jq '.[] | {id,name,status,stage}'
glab api "projects/$PROJECT_ID/environments?name=$ENV_NAME" \
--jq '.[] | {id,name,state,external_url,tier,last_deployment:(.last_deployment.id // null)}'
glab api "projects/$PROJECT_ID/deployments?environment=$ENV_NAME&order_by=finished_at&sort=desc" \
--jq '.[] | {id,status,sha,deployable_job_id:.deployable.id,finished_at}'
8. Prove commit/artifact/environment identity
Required proof:
- Pipeline SHA equals
CHECKPOINT_SHA. - Build job produced
release.txtand checksum. - Deploy job log shows checksum verification success.
-
Newest successful deployment SHA equals
CHECKPOINT_SHA. -
Deployment’s job ID is the
deploy_reviewjob from that pipeline. -
Environment is
available, but the.invalidURL demonstrates that GitLab state is not external health.
9. Exercise the stop transition
Trigger stop_review. Predict
available → stopped, then verify via the Environments
API/UI. Preserve the stop-job ID/status. In production you would
additionally query the target platform to prove the resource is
gone.
10. Redeploy the same known artifact state
Run deploy_review again from the same pipeline if
available/retry semantics fit your instance, or create a new
pipeline at the same commit without rebuilding the release artifact
in a way that changes identity. The key invariant is the
checksum/commit identity remains the intended known state. Record
the new deployment ID and verify the environment returns to
available.
11. Trigger the intentional failed deployment
Run the manual deploy_broken job. It verifies the same
artifact then exits 42. Preserve:
-
job ID and stderr
synthetic target rejected deployment; - job/pipeline SHA;
- deployment status for the environment;
- the last previously successful deployment ID.
Do not add allow_failure, retry immediately, or edit
history. The failure is the diagnostic evidence required by the
prompt.
12. Recover to a known artifact
Remove/repair the deliberate exit 42 path and run a
normal deployment using the same build_release artifact
identity. If your instance/UI supports rollback/redeploy from
environment history, inspect that path and explain that it creates a
new deployment/job pointing to the selected commit. Do not rebuild
from mutable dependencies merely to claim rollback.
Verify the newest deployment is successful and its commit SHA/checksum match the intended known state.
13. Simulate the paid governance prediction
Without changing tier/settings, document what would happen if this environment were Premium/Ultimate protected with one required approval:
| Prediction | Expected behavior |
|---|---|
| Unauthorized Developer starts deploy | GitLab protection denies/blocks according to allowed deployer rules. |
| Eligible deployment awaiting approval | Deployment is blocked until required approval is granted. |
| Pipeline triggerer approves own deployment | Denied by default unless self-approval option is enabled. |
| Required approval is granted | Approval state is satisfied, but approval itself does not automatically run the deployment job. |
| External credential used outside GitLab | GitLab environment protection cannot stop it; target IAM must enforce authorization. |
14. Preserve the environment history
Environment history should now contain a sequence that tells the story: successful deployment → stop transition → successful redeploy → failed deployment → successful recovery. Do not delete this history just to make the lab appear perfect. The failed record proves your diagnostic workflow.
15. Cleanup / rollback
First stop the environment. Remove any synthetic variable if you created one. Restore/remove Chapter 17 CI changes. Verify the exact remote ref before deletion:
git switch main
git ls-remote --heads origin refs/heads/ch17/checkpoint
# Delete only the verified disposable branch:
git push origin --delete ch17/checkpoint
git branch -D ch17/checkpoint
If the project exists only for the lab, deleting it is optional and destructive; first confirm no valuable issues, artifacts, environments, variables, or shared history remain. Otherwise keep the stopped environment history for learning/audit and rely on short artifact retention.
16. Final verification checklist
- Environment/deployment records are tied to the expected commit and job IDs.
- Deploy jobs verify one known artifact checksum rather than rebuilding silently.
- The synthetic URL is clearly non-real and no cloud/Kubernetes target was touched.
- Stop action changed GitLab environment state and produced cleanup evidence.
- The intentional deployment failure remains visible and its cause is understood.
- Recovery created a new successful deployment using the known state.
- No secrets, tokens, private environment dumps, real infrastructure identifiers, or runner credentials were exposed.
- Disposable ref/config/variables are removed and the environment is stopped.
17. What Chapter 17 adds to a production operating model
You can now represent deployment state as a chain of evidence rather than a green job: source/commit → immutable artifact → deployment job → GitLab deployment → environment → independently verified target. You can also model ephemeral review lifecycle, stop/TTL behavior, environment-scoped runtime controls, rollback identity, and the boundary between Free mechanics and paid deployment governance.
Chapter 18 adds the next control dimension: who or what may delay, manually trigger, schedule, freeze, or supersede a release path over time.
Knowledge check
Why does the checkpoint deliberately keep the failed deployment in history?
It is audit/diagnostic evidence showing the exact failed job, commit, environment, and recovery sequence.
What two predictions must be made before the first deploy?
At minimum the expected environment state transition and the exact commit/artifact identity; the lab also predicts deployment/job records and concurrency.
Does stopping the GitLab environment prove a real cloud resource was destroyed?
No. Production cleanup needs independent external verification; the lab has no real external resource.
Why is deploy_broken not marked
allow_failure?
The non-zero deployment result is the signal being diagnosed; hiding it would destroy the learning evidence.
After a paid deployment approval is granted, what current behavior still matters?
The corresponding deployment job must still be run; approval alone does not automatically execute it.
What makes the recovery a known-state recovery rather than a rebuild?
The deployment verifies and reuses the same intended commit/artifact checksum identity instead of producing new bytes from mutable inputs.
Checkpoint summary
You built an auditable GitLab deployment model without external infrastructure: identified artifact, dynamic environment, deployment record, lifecycle stop, deliberate failed deployment, known-state recovery, and preserved history. You can now distinguish what GitLab proves from what an external platform must prove—and that distinction is the foundation for safe release 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.