Chapter 18Lesson 05~320 minutes

Checkpoint Lab — Manual Jobs, Delayed Jobs, Scheduled Pipelines, Rollbacks, Freeze Windows, and Release Controls

Prove the chapter end to end with a synthetic controlled release, superseded stale path, schedule/freeze evidence, recovery, and targeted cleanup.

CheckpointRelease controlPredictionSupersessionRollbackEvidence

Checkpoint objectives

  • Predict actor/time/source/SHA/artifact state before every release-control transition.
  • Operate one manual gate and one timed control against disposable output.
  • Capture one safe stale/missing release path and diagnose the responsible control.
  • Model a freeze exception and rollback without production infrastructure or paid governance.
  • Remove schedules/freeze objects/refs by exact identity and preserve the audit story.
Availability baseline (verified 2026-08-22 against GitLab 19.3 current documentation). Manual jobs, manual_confirmation, delayed jobs with start_in, pipeline schedules, deployment freeze periods / CI_DEPLOY_FREEZE, environment/deployment history, the project setting that prevents outdated deployment jobs, and rollback/retry controls are available on GitLab Free/Premium/Ultimate across GitLab.com, Self-Managed, and Dedicated. Protected environments and deployment approvals—the fine-grained “only these people may deploy” layer—remain Premium/Ultimate. The mandatory chapter path uses only a disposable project, synthetic artifacts/environments, and tiny jobs. No production endpoint, cloud account, paid tier, runner registration, real credential, or release publication is required.

1. Checkpoint scenario

You are release engineer for a synthetic application. Every “deployment” is only a checksum verification plus a GitLab environment record at release-control/ch18-checkpoint. The workflow must prove that humans, time gates, schedules, freeze policy, stale-path prevention, and rollback all preserve one invariant: the environment may change only through an identified release path tied to a known commit/artifact.

2. Preflight

Item Required
Offering/tier GitLab.com, Self-Managed, or Dedicated; Free is sufficient for mandatory path.
Role Developer for branch/pipeline; Maintainer/Owner only if creating schedule/freeze or changing stale-deployment setting.
Runner Tiny eligible runner or no-runner fixture/static-validation path.
External target None; reserved example.invalid URL only.
Secrets None. No manual job variables containing credentials.
Paid controls Protected environment / deployment approvals are architecture fixtures only.

3. Capture before-state

PROJECT_ID="12345678"

glab api "projects/$PROJECT_ID" \
  --jq '{default_branch,ci_forward_deployment_enabled,ci_forward_deployment_rollback_allowed}'
glab api "projects/$PROJECT_ID/pipeline_schedules" --paginate \
  --jq '.[] | {id,description,ref,active,owner:.owner.username}'
glab api "projects/$PROJECT_ID/freeze_periods" --paginate \
  --jq '.[] | {id,freeze_start,freeze_end,cron_timezone}'
glab api "projects/$PROJECT_ID/environments?name=release-control/ch18-checkpoint" \
  --jq '.[] | {id,name,state,last_deployment:(.last_deployment.id // null)}'

Choose unique resource names/IDs. Record whether Prevent outdated deployment jobs is already enabled; do not change a shared project setting without ownership/approval.

4. Write predictions before execution

Event Prediction
Build Creates one checksum-addressed synthetic artifact tied to checkpoint SHA.
Manual gate not run Pipeline remains blocked at release gate.
Manual gate run by eligible actor Gate succeeds; delayed deployment becomes scheduled.
Delayed job timer active No runner slot is used until it becomes runnable; later stage does not progress through this gate.
Newer pipeline deploys first Older delayed deployment must be canceled/unscheduled or rejected as stale.
Freeze fixture true Routine deploy changes to documented manual exception behavior.
Rollback/recovery Creates a new deployment action using known prior artifact identity; history remains intact.

5. Checkpoint pipeline

stages: [build, gate, deploy, verify]

build_release:
  stage: build
  script:
    - mkdir -p out
    - printf 'sha=%s\npipeline=%s\n' "$CI_COMMIT_SHA" "$CI_PIPELINE_ID" > out/release.txt
    - sha256sum out/release.txt > out/release.txt.sha256
  artifacts:
    name: "ch18-$CI_COMMIT_SHORT_SHA"
    paths: [out/release.txt, out/release.txt.sha256]
    expire_in: 2 days
    access: developer

release_gate:
  stage: gate
  needs:
    - job: build_release
      artifacts: true
  script:
    - sha256sum -c out/release.txt.sha256
    - echo "gate_actor_confirmed=true"
  when: manual
  allow_failure: false
  manual_confirmation: "Proceed with synthetic release?"

release_delayed:
  stage: deploy
  needs:
    - job: release_gate
      artifacts: true
  resource_group: ch18-checkpoint
  script:
    - sha256sum -c out/release.txt.sha256
    - echo "synthetic_deploy=true"
  when: delayed
  start_in: 45 seconds
  environment:
    name: release-control/ch18-checkpoint
    url: https://ch18-checkpoint.example.invalid

verify_release:
  stage: verify
  needs: [release_delayed]
  script:
    - echo "verify_pipeline=$CI_PIPELINE_ID"
    - echo "verify_sha=$CI_COMMIT_SHA"

6. Validate and reason about the merged YAML

Use CI Lint/Pipeline Editor. Confirm no secret, external endpoint, registry push, package publish, token creation, or production tag exists. Confirm the gate is blocking, the delayed job is bounded, all deployment paths share the same synthetic environment/resource-group identity, and the artifact checksum is checked before every simulated deploy.

7. Create the checkpoint ref

git switch -c ch18/checkpoint
git add .gitlab-ci.yml
git diff --cached --check
git diff --cached
git commit -m "ch18: controlled release checkpoint"
CHECKPOINT_SHA="$(git rev-parse HEAD)"
printf 'checkpoint_sha=%s\n' "$CHECKPOINT_SHA"
git push -u origin ch18/checkpoint

8. Prove the manual gate is blocking

Before running it, record the pipeline status and release_gate status. Verify release_delayed has not executed. Then run the gate without any ad-hoc variables. Record the actor, job ID, and SHA.

Prediction check: the gate succeeds only after a human starts it; the pipeline does not silently continue while it is waiting.

9. Exercise the timed path

After the gate succeeds, inspect release_delayed. Record its scheduled/delayed state and timestamps. For the first pipeline, do not let it deploy yet; this pipeline becomes the intentionally stale path.

10. Create a newer pipeline and supersede the stale path

Make a harmless change to the synthetic payload (for example add generation=2), commit, push, run the manual gate in Pipeline B, and allow its delayed deployment to complete first. Record Pipeline B’s SHA/artifact checksum/deployment ID.

Now return to Pipeline A. Choose one safe path:

  • Preferred lab action: Unschedule/cancel Pipeline A’s delayed deployment after preserving its IDs; document “superseded by Pipeline B.”
  • If testing stale-deployment setting in a dedicated project: leave it runnable and prove GitLab rejects/disables it as outdated after the newer deployment. Do not weaken the setting to force it through.

This is the required intentionally missing/rejected release path.

11. Capture stale-path evidence

# Example selected metadata; use actual IDs.
glab api "projects/$PROJECT_ID/pipelines/$PIPELINE_A/jobs" --paginate \
  --jq '.[] | select(.name=="release_delayed") | {id,name,status,sha,created_at,started_at,finished_at}'
glab api "projects/$PROJECT_ID/pipelines/$PIPELINE_B/jobs" --paginate \
  --jq '.[] | select(.name=="release_delayed") | {id,name,status,sha,created_at,started_at,finished_at}'
glab api "projects/$PROJECT_ID/deployments?environment=release-control/ch18-checkpoint&order_by=finished_at&sort=desc" \
  --jq '.[] | {id,status,sha,job:(.deployable.id // null),finished_at}'

Explain which control stopped the old path: operator unschedule/cancel, GitLab outdated-deployment policy, or both. “It didn’t run” is not enough.

12. Add one inactive schedule and record ownership

Create ch18 checkpoint schedule targeting ch18/checkpoint, cron 23 4 * * *, UTC, inactive. Capture schedule ID, owner, ref, cron, and active state. Do not add secret variables. If you choose a manual one-time Run for additional evidence, record that source=schedule and that the one-time run used the triggering user’s permissions.

13. Freeze-policy exercise

Do not manipulate the system clock. Use this policy fixture:

{
  "freeze_period": {"start": "0 22 * * 5", "end": "0 8 * * 1", "timezone": "UTC"},
  "pipeline_pre_variable": {"CI_DEPLOY_FREEZE": "true"},
  "expected_release_behavior": "manual exception path only",
  "required_exception_evidence": ["actor", "reason", "artifact_digest", "post_deploy_verification"]
}

Optional live exercise in a dedicated project: create a disposable freeze period covering the current time, run one tiny pipeline, prove CI_DEPLOY_FREEZE affects job inclusion, then delete that exact freeze ID. Do not leave broad freeze periods behind.

14. Simulate a failed release and recover

Create a manual synthetic deployment job that verifies Pipeline B’s known artifact then exits 42. Preserve the failure. Recovery is a new deployment path that reuses the selected known artifact/commit identity. If you instead rebuild, explicitly record that it is a rebuild and compare checksums.

Required recovery evidence: original known-good checksum, failed job ID, recovery job ID, recovery deployment ID, environment state, and post-recovery verification.

15. Document the emergency exception

Write a short evidence record—not a secret-bearing comment:

Exception ID: CH18-LAB-001
Reason: synthetic recovery after deliberate failed deployment
Actor: <record GitLab username from job evidence>
Pipeline/SHA: <IDs only>
Artifact digest: <SHA-256 only>
Freeze state: simulated=true
Authorization model: disposable Free project; no protected-environment approval claim
Result: recovered synthetic environment; external side effects=none

16. Cleanup and prove cleanup

Delete the inactive schedule by exact captured ID. Delete an optional freeze period only by exact captured ID. Stop the synthetic environment. Restore/remove Chapter 18 YAML. Verify then delete the disposable branch:

git switch main
git ls-remote --heads origin refs/heads/ch18/checkpoint
# Exact disposable ref only:
git push origin --delete ch18/checkpoint
git branch -D ch18/checkpoint

# Verify schedule list no longer contains the captured ID.
glab api "projects/$PROJECT_ID/pipeline_schedules" --paginate \
  --jq '.[] | {id,description,ref,active}'
# Verify freeze list contains no CH18 disposable ID (if one was created).
glab api "projects/$PROJECT_ID/freeze_periods" --paginate \
  --jq '.[] | {id,freeze_start,freeze_end,cron_timezone}'

17. Final verification checklist

  • Manual gate behavior and actor were predicted and verified.
  • Delayed job state/timestamps were captured without treating delay as freshness.
  • One stale path was safely superseded/rejected with original evidence intact.
  • Schedule owner/ref/cron/active state were captured; schedule was removed.
  • Freeze behavior was modeled as policy signal, not authorization.
  • Rollback/recovery used or explicitly compared known artifact identity.
  • No real secret, token, cloud endpoint, registry publication, protected-setting bypass, or runner registration occurred.
  • Disposable server-side objects and refs were removed by exact ID/name.

18. What Chapter 18 adds to the production operating model

You now have a release state machine that accounts for people, time, ownership, freshness, and recovery. A release path can wait for a human, wait for elapsed time, recur from a schedule, respect a freeze policy, reject stale deployment, and recover to known state—all while preserving GitLab evidence rather than hiding exceptions.

Chapter 19 moves from control of a single pipeline to composition across pipelines and projects: parent-child pipelines, multi-project pipelines, trigger tokens, and downstream orchestration.

Knowledge check

Why is Pipeline A preserved after it becomes stale?

What two things must be predicted before running the checkpoint?

Why is the schedule inactive?

Does the freeze fixture prove nobody could deploy with another credential/path?

How do you prove the recovery is a rollback to known state?

What is the correct cleanup rule for schedules and freeze periods?

Checkpoint summary

You operated a complete controlled-release model without production risk: human gate, timed gate, schedule identity, freeze policy, stale-path supersession, deliberate failure, known-state recovery, and targeted cleanup. The core lesson is that release safety emerges from composing explicit identities and state transitions—not from one approval button.

Official references

Next chapter

Parent-child pipelines, multi-project pipelines, trigger tokens, and composition

Chapter 19 takes the release-control evidence model across pipeline boundaries and teaches how downstream triggers, identities, statuses, artifacts, and permissions compose safely.

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.