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.
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.
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:
- 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.
- 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_IMAGEor equivalent scanned image reference; - resolved/expected image digest;
-
checksum of
gl-container-scanning-report.jsonif 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?
The exact OCI manifest/index digest in the consumption reference matches the digest captured from registry metadata.
Why must cleanup policy be written before deletion?
Because deletion can remove rollback/release state. The policy must classify deployed/release/incident-held images before an operator removes tags.
Your scan says zero findings but production digest differs from the scan digest. Is the gate satisfied?
No. The scan is evidence for a different image; identity mismatch invalidates the assurance.
What persistent credential must be revoked in the mandatory CI path?
None. CI_REGISTRY_PASSWORD is job-only. Only an
optional deploy/PAT/project token would require explicit
revocation.
Why do you verify tag absence after deletion instead of assuming the CLI succeeded?
Independent read-after-write verification proves the intended registry object changed and catches async/permission/wrong-repository mistakes.
What is the next production concept after image identity?
Release governance: tying Git tags/releases, release assets/changelogs, pipeline evidence, and deployment traceability to exact artifacts/images.
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:
- GitLab Docs — Container Registry
- GitLab Docs — Authenticate with the Container Registry
- GitLab Docs — Reduce Container Registry storage
- GitLab Docs — Protected container repositories
- GitLab Docs — Protected container tags
- GitLab Docs — Immutable container tags
- GitLab Docs — Container Registry API
- GitLab Docs — Container scanning
- GitLab Docs — Container Registry metadata database
- GitLab Docs — Predefined CI/CD variables
- GitLab CLI — container-registry repository list
- GitLab CLI — container-registry tag list
- GitLab CLI — container-registry tag view
- GitLab CLI — container-registry tag delete
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.