Container Registry, OCI Images, Cleanup, Authentication, Vulnerability Scanning, and Retention: Concepts, Architecture, and Mental Model
Model GitLab Container Registry state using OCI repositories, mutable tags, immutable digests, manifests, authentication scopes, cleanup policy, scanning evidence, and Self-Managed storage boundaries.
Learning objectives
- Distinguish project registry repositories, OCI manifests/indexes, tags, and digests.
- Explain why digest identity survives tag movement and supports rollback/audit.
- Choose short-lived CI registry identity versus persistent registry tokens with least privilege.
- Separate cleanup policy from physical storage garbage collection.
- Bind container-scanning evidence to the exact image identity it assessed.
1. The practical problem: “image:latest” is not a release identity
Chapter 22 treated packages as governed distribution objects.
Container images are also release artifacts, but OCI adds another
layer of indirection: a human-friendly tag points
to a manifest, while a content-addressed
digest identifies exact manifest bytes. If a
deployment records only service:latest, the same text
can resolve to different content tomorrow.
This chapter therefore treats the registry as production state. The useful question is not “did we push an image?” but “which project repository owns it, which manifest digest was produced, what source and pipeline created it, who may mutate its tags, what scan evidence belongs to that digest, and what retention rule keeps rollback possible?”
2. Registry object model: project → repository → manifest → tag/digest
| Object | Meaning | Why operators care |
|---|---|---|
| GitLab project | Authorization, CI/CD, settings, registry ownership boundary. | Determines who can publish/delete and which CI identity exists. |
| Container repository | OCI image name beneath the project registry path. |
Separates services/variants such as api and
worker.
|
| Manifest / index | OCI descriptor for one platform image or a multi-platform index. | Digest identifies exact manifest/index bytes. |
| Tag |
Mutable human-readable pointer such as
v1.4.0 or latest.
|
Convenient selector, weaker identity unless immutability is enforced. |
| Digest |
Content-addressed identifier such as sha256:….
|
Stable evidence for deployment, rollback, scan association, and provenance. |
Layers may be shared across manifests, so repository/tag deletion and physical storage reclamation are not the same operation.
3. Mental model: source and pipeline create a registry object; deployment consumes a digest
flowchart TD G[Git commit / ref] --> P[Pipeline + build job] P -->|CI_REGISTRY_USER + short-lived password| R[Project Container Registry] R --> T[Tag: commit-abc123] T --> D[Manifest digest sha256:...] D --> S[Container scan report] D --> E[Deployment / runtime] C[Cleanup + protection policy] --> T A[Self-Managed storage + GC] --> R
The pipeline publishes into a project-owned registry path. The tag is a name, the digest is the content identity. Scanning and deployments are useful only when they can be tied back to that identity. Cleanup changes tag visibility; Self-Managed garbage collection determines when unreferenced storage is actually reclaimed.
4. OCI identity: tag equality is weaker than digest equality
An OCI reference may be written as
registry.example.invalid/group/project/api:v1 or as
registry.example.invalid/group/project/api@sha256:<digest>. The first asks the registry what v1 points to at
request time. The second names the manifest content directly.
registry.example.invalid/platform/payments/api:v1.4.0
registry.example.invalid/platform/payments/api@sha256:0123456789abcdef...
A platform-specific image can have one digest while a multi-architecture index has another. Record which digest your deployment mechanism resolves and uses; do not compare unrelated manifest and platform-manifest digests as if they were the same object.
5. Authentication is short-lived; authorization is registry-scope plus project permission
Current registry token methods include personal, project, group, and
deploy tokens. For token-based access, pull requires
read_registry; push requires both
read_registry and write_registry. In CI,
GitLab exposes CI_REGISTRY_USER and
CI_REGISTRY_PASSWORD; the password is job-only, has the
same value as CI_JOB_TOKEN, and expires with the job.
| Identity | Best use | Risk / control |
|---|---|---|
CI_REGISTRY_USER +
CI_REGISTRY_PASSWORD
|
Publish from the current pipeline. | Ephemeral; preferred mandatory lab identity. |
| Deploy token | Long-lived machine pull/push outside CI. |
Give only read_registry unless publishing is
required; expire/revoke.
|
| Project/group/PAT | API/human automation where documented. | Persistent and often broader; avoid when job identity is sufficient. |
| Username/password | Limited cases without 2FA. | Do not build production automation around a reusable account password. |
Use --password-stdin; do not place a registry password
on a command line, in source, or in a trace artifact.
6. Read-only inspection comes before publish or delete
Prove the project registry is enabled and inspect current repositories before changing anything:
git rev-parse HEAD
glab container-registry repository list --output json
# For a known repository ID:
glab container-registry repository view <repository-id> --include-tags --output json
glab container-registry tag list <repository-id> --details --output json
The current glab container-registry commands operate
against GitLab’s registry metadata API. Detailed tag listing adds
digest, size, and creation data. Record project, repository ID/path,
tag, digest, commit SHA, and pipeline ID together.
7. Cleanup policy removes tags; storage reclamation is a separate layer
GitLab project cleanup policies select tags by name/age and keep
rules. Current behavior always keeps latest, excludes
configured keep patterns and recent tags, and also excludes
protected/immutable tags. Deleting a tag can leave manifest/layer
data referenced elsewhere or awaiting garbage collection.
On GitLab.com/Dedicated, the service operates storage reclamation. On Self-Managed, administrators own registry storage and garbage-collection architecture. With the registry metadata database, GitLab supports online garbage collection; legacy/offline modes have different operational procedures. A project Maintainer should not run administrator garbage-collection commands merely because a cleanup policy removed tags.
8. Protection and immutability are different controls
Protected container repositories restrict who may push to matching repository paths and are Free across current offerings. Protected container tags restrict who may push/delete matching tags and are currently documented for GitLab.com/Self-Managed. Immutable container tags prevent matching existing tags from being updated or deleted, but are Ultimate-only on GitLab.com/Self-Managed.
Digest-based deployment remains useful even if a tag is protected or immutable: permission policy controls mutation; digest identity proves which manifest was selected.
9. A scan report is evidence about an image, not proof the image is trusted
Current GitLab container scanning is Free/Premium/Ultimate and uses
a GitLab-managed CI template around the Trivy analyzer. The job can
emit gl-container-scanning-report.json and a CycloneDX
SBOM. Richer vulnerability-management surfaces vary by tier.
A “green scan” does not prove source provenance, absence of malware, correct base-image policy, correct digest selection, or future vulnerability status. Preserve the scanned image reference/digest, analyzer version/evidence, commit, and pipeline so findings cannot be accidentally attached to a different tag incarnation.
10. Production invariant: the release record must survive tag movement
A production handoff should be reconstructible from immutable evidence: source commit → pipeline → image repository → manifest digest → scan/report → deployment. Tags can remain for humans, but deployment/rollback decisions should be verifiable against the digest actually used.
Knowledge check
Why can two deployments both say api:latest yet
run different software?
A tag is a mutable pointer. If it is moved between pulls, the same textual tag resolves to a different manifest digest.
What is the minimum token scope for pushing to the Container Registry?
Current docs require both read_registry and
write_registry. Pull-only access needs
read_registry.
Does deleting a tag immediately guarantee that all corresponding blobs are physically gone?
No. Tags, manifests/layers, sharing, and garbage collection are different lifecycle layers.
Why is an image scan not equivalent to image trust?
Scanning detects known vulnerability classes in the scanned image; it does not by itself prove source provenance, correct digest selection, authorization, malware absence, or deployment identity.
What evidence should connect a deployment back to CI?
At minimum project/repository, exact manifest digest, source commit, producing pipeline/job, and the relevant scan/provenance evidence.
Summary
The operating model is now explicit: GitLab owns project-level registry authorization, OCI defines repositories/manifests/tags/digests, CI supplies short-lived registry identity, cleanup controls tag retention, and scanning produces security evidence. The digest is the durable content coordinate tying those layers together.
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.