Chapter 17Lesson 05~310 minutes

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.

CheckpointDeployment recordReview environmentFailureRecoveryEvidence

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.
Availability baseline (verified 2026-08-21 against current GitLab documentation). Environments, deployment records, Review Apps/dynamic environments, 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.txt and checksum.
  • Deploy job log shows checksum verification success.
  • Newest successful deployment SHA equals CHECKPOINT_SHA.
  • Deployment’s job ID is the deploy_review job from that pipeline.
  • Environment is available, but the .invalid URL 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.

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?

What two predictions must be made before the first deploy?

Does stopping the GitLab environment prove a real cloud resource was destroyed?

Why is deploy_broken not marked allow_failure?

After a paid deployment approval is granted, what current behavior still matters?

What makes the recovery a known-state recovery rather than a rebuild?

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

Next chapter

Manual jobs, delayed jobs, schedules, rollback controls, and freeze windows

Chapter 18 adds people/time release gates to the environment model you just built, including manual and delayed execution, scheduled pipeline creation, freeze policy, stale-release handling, and controlled rollback paths.

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.