Chapter 23Lesson 05~350 minutes

Checkpoint Lab — Container Registry, OCI Images, Cleanup, Authentication, Vulnerability Scanning, and Retention

Operate one disposable OCI image lifecycle from source and publish evidence through digest verification, scan evidence, digest-based consumption, retention analysis, and verified cleanup.

CheckpointOCI lifecycleDigestScan evidenceRetentionCleanup

Learning objectives

  • Predict repository/tag and lifecycle state before mutation.
  • Record the exact manifest digest independently from the tag.
  • Bind scan evidence to the image and construct a digest-based deployment reference.
  • Write retention policy before destructive cleanup and verify cleanup independently.
  • Produce a production-style source→pipeline→digest→scan→deployment evidence record.
Availability baseline (verified 2026-08-22 against current GitLab 19.3 documentation). The integrated Container Registry is Free/Premium/Ultimate on GitLab.com, Self-Managed, and Dedicated; Self-Managed administrators must enable and operate it. Registry authentication supports job-scoped CI credentials and token credentials with registry scopes. Cleanup policies are Free across all three offerings. Container scanning is Free/Premium/Ultimate, but some vulnerability-management presentation/governance capabilities vary by tier. Protected container repositories are Free across all three offerings. Protected container tags are Free on GitLab.com and Self-Managed with the current registry backend prerequisites. Immutable container tags are Ultimate-only on GitLab.com/Self-Managed. The mandatory labs therefore require neither Ultimate nor Self-Managed administration, privileged runners, cloud spend, nor a persistent registry credential.

1. Checkpoint mission and acceptance criteria

Operate one disposable OCI image lifecycle: source → image reference → registry metadata → digest → scan evidence or labeled fixture → digest-based consumption reasoning → retention decision → cleanup. You may publish live only when safe free-compatible tooling exists; otherwise use the fixture lane while still proving every identity and policy decision.

Success means you can independently answer “what exact image did this pipeline create and what exact image would a deployment consume?”

2. Preflight assumptions

  • GitLab Free project; Developer for publishing, Maintainer/Owner where project cleanup/protection settings are inspected or changed.
  • Container Registry enabled. Self-Managed registry enablement/storage administration is outside the mandatory lab.
  • No production projects, tags, runners, registries, deployment credentials, or cloud accounts.
  • No persistent token required. Optional deploy token must be disposable, minimally scoped, and revoked before completion.
  • If no safe builder/runner compute exists, use the supplied OCI and scan fixtures.

3. Create the evidence directory and capture baseline state

Capture source/project/registry state first:

mkdir -p evidence/ch23
git status --short --branch > evidence/ch23/git-status.txt
git rev-parse HEAD > evidence/ch23/source-sha.txt
glab container-registry repository list --output json > evidence/ch23/registry-before.json
sha256sum evidence/ch23/source-sha.txt evidence/ch23/registry-before.json > evidence/ch23/baseline.sha256

4. Make two required predictions before mutation

Write evidence/ch23/predictions.md with at least:

  1. Identity prediction: the repository path and commit-derived tag you expect, and the rule that the digest—not the tag text—will be the authoritative content identity.
  2. Lifecycle prediction: deleting the lab tag should remove the tag from GitLab metadata, but physical blob reclamation may be later/separate and must not be forced from project CI.

Also predict which credential type performs push/pull and which actor has delete permission.

5. Live publication path or explicit simulation

If safe local/runner tooling is available, publish one tiny image to $CI_REGISTRY_IMAGE/ch23-checkpoint:$CI_COMMIT_SHA using CI_REGISTRY_USER/CI_REGISTRY_PASSWORD in CI or a disposable local credential. Keep secrets off command arguments and logs. If not, create this fixture:

{
  "project":"learner-example/ch23-registry-lab",
  "repository":"registry.example.invalid/learner-example/ch23-registry-lab/ch23-checkpoint",
  "tag":"1111111111111111111111111111111111111111",
  "digest":"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
  "pipeline_id":23001,
  "scan_report_sha256":"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
}

Label fixture evidence as simulated. Never present it as a real push or scan.

6. Capture registry identity independently

For a live image:

glab container-registry repository list --include-tags-count --output json > evidence/ch23/repositories.json
REPO_ID="$(jq -r 'map(select(.path | endswith("/ch23-checkpoint")))[0].id // empty' evidence/ch23/repositories.json)"
test -n "$REPO_ID"
glab container-registry tag list "$REPO_ID" --details --per-page 100 --output json > evidence/ch23/tags.json
TAG="$(cat evidence/ch23/source-sha.txt)"
DIGEST="$(jq -r --arg t "$TAG" 'map(select(.name==$t))[0].digest // empty' evidence/ch23/tags.json)"
test -n "$DIGEST"
printf 'repo_id=%s tag=%s digest=%s\n' "$REPO_ID" "$TAG" "$DIGEST" | tee evidence/ch23/identity.txt

Compare the actual repository/tag with your prediction. A mismatch stops the lab until explained.

7. Demonstrate why tag equality is weaker than digest equality

Use either live metadata or the fixture. Write two observations of a human tag such as candidate pointing to digest A and then digest B. Do not overwrite a protected/valuable tag; this is a disposable or synthetic demonstration.

observation_1: candidate -> sha256:aaaaaaaa...
observation_2: candidate -> sha256:bbbbbbbb...

Conclusion: tag text stayed equal; content identity did not.

Then write the production control: deployment/release evidence stores the exact digest resolved at promotion time.

8. Produce or inspect scan evidence and bind it to the digest

Use the current GitLab container-scanning template when prerequisites are available, otherwise a labeled fixture. Record:

  • pipeline/job ID and source SHA;
  • CS_IMAGE or equivalent scanned image reference;
  • resolved/expected image digest;
  • checksum of gl-container-scanning-report.json if present;
  • analyzer/template version evidence available in job logs.

Do not reduce the checkpoint to a vulnerability count. The identity link is part of the result.

9. Prove digest-based consumption

With live tooling, pull by digest or at minimum construct and validate the exact reference:

IMAGE="$CI_REGISTRY_IMAGE/ch23-checkpoint"
DIGEST="sha256:<verified-digest>"
printf 'deployment_reference=%s@%s\n' "$IMAGE" "$DIGEST" > evidence/ch23/deployment-reference.txt
# Optional local pull when safe:
# docker pull "$IMAGE@$DIGEST"

Store the digest reference with the source/pipeline evidence. A release tag may be added for humans, but the digest remains the rollback coordinate.

10. Write a retention decision before deleting anything

Document a policy such as:

  • commit/review images: 14–30 days after branch/MR closure;
  • release images: retain for the rollback/SLO/compliance window;
  • currently deployed digests: never remove merely because they are old;
  • incident hold: suspend cleanup for affected digests/tags until evidence preservation is complete.

For this synthetic image, record “no downstream deployment references; safe to remove after evidence capture.”

11. Optional persistent credential exercise: prove revocation, not value

If you deliberately created a deploy token for an external pull demonstration, record only its name, scope, expiry, and resource owner. Revoke it now. Verify a subsequent authentication attempt fails without putting the token value into the transcript. The mandatory CI path has no persistent credential to revoke because its registry password expires with the job.

12. Delete only verified disposable registry state

Before deletion, re-query the exact tag and compare the digest to evidence/ch23/identity.txt. Then:

REPO_ID="<verified-repository-id>"
TAG="<verified-disposable-tag>"
glab container-registry tag view "$REPO_ID" "$TAG" --output json > evidence/ch23/tag-final-check.json
# Destructive, disposable registry only:
glab container-registry tag delete "$REPO_ID" "$TAG" --yes
if glab container-registry tag view "$REPO_ID" "$TAG" --output json >/dev/null 2>&1; then
  echo "cleanup verification failed: tag remains" >&2
  exit 1
fi
echo "verified registry tag cleanup"

If you delete the entire disposable repository instead, use the explicit repository-delete command only after re-listing all tags and confirming no consumer references. Do not trigger Self-Managed garbage collection from this project lab.

13. Independent verification checklist

  • Source SHA and project were captured before registry mutation.
  • Repository path and tag matched the prediction or the mismatch was explained.
  • Exact manifest digest was recorded independently of the tag.
  • Scan evidence/fixture was explicitly bound to the intended image identity.
  • Deployment reference used the digest, not only a mutable tag.
  • Retention rule considered deployed/rollback images before cleanup.
  • Any optional persistent token was revoked and verified unusable.
  • Disposable tag/repository deletion was verified through a second read.
  • No production image, registry, runner privilege, cloud resource, or real secret was touched.

14. Production evidence record

Your final record should be sufficient for another operator to answer:

source_sha=<40-hex>
pipeline_id=<id>
project=<namespace/project>
registry_repository=<host/project/ch23-checkpoint>
tag=<release-or-commit-tag>
manifest_digest=sha256:<digest>
scan_report_sha256=<hash-or-fixture-label>
deployment_reference=<repository>@sha256:<digest>
retention_class=<ephemeral|release|deployed|incident-hold>
cleanup_result=<verified removed|retained with reason>

15. What Chapter 23 adds to the production GitLab operating model

You can now govern OCI images as first-class release state: least-privilege publishing, digest-based identity, scan-to-image evidence, explicit retention, protected/immutable controls where available, and a clean boundary between project-level tag lifecycle and Self-Managed storage garbage collection. Chapter 24 builds on this identity to create GitLab Releases, tags, changelogs, assets, and deployment traceability.

Knowledge check

What is the strongest proof that the image consumed after the checkpoint is the one you recorded?

Why must cleanup policy be written before deletion?

Your scan says zero findings but production digest differs from the scan digest. Is the gate satisfied?

What persistent credential must be revoked in the mandatory CI path?

Why do you verify tag absence after deletion instead of assuming the CLI succeeded?

What is the next production concept after image identity?

Summary

The checkpoint completes the OCI lifecycle without depending on privileged infrastructure: predict, inspect, publish or simulate, capture digest, bind scanning evidence, consume by digest, justify retention, revoke optional credentials, and verify cleanup. The result is an auditable image identity rather than a collection of registry commands.

Official references

Primary sources used for the current GitLab 19.3 behavior taught in this lesson:

Next chapter

Chapter 24 — Releases, Tags, Release CLI, Evidence, Changelogs, and Deployment Traceability

Turn exact package/image/source identities into a governed release record with reproducible evidence and traceable deployment state.

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.