Chapter 23Lesson 01~170 minutes

Container Registry, Package Registry, Generic Packages, Dependency Proxy, and Artifact Promotion: Concepts, Architecture, and Mental Model

Model GitLab registries as versioned distribution systems where producer SHA, authentication identity, immutable version or digest, consumer pull, promotion, and retention remain independently provable.

RegistriesPackagesDigestsPromotionProvenance

Learning objectives

  • Distinguish CI artifacts from package/container registries and explain when a durable distribution coordinate is required.
  • Trace source SHA → producer job → authenticated publication → version/tag/digest → consumer pull → promotion → retention.
  • Explain why a container tag is a convenient label but a manifest digest is the immutable distribution identity.
  • Explain why a Dependency Proxy reduces upstream pulls but does not establish provenance.
  • Inspect registry/package identity and authorization without printing credentials or mutating unrelated objects.

1. The practical problem: a green build does not make an artifact safely distributable

Chapter 22 bound a release record to an exact tag, source SHA, and verified artifact digest. Chapter 23 asks where those bytes should live so downstream pipelines and environments can retrieve them repeatedly without rebuilding. A CI artifact is excellent pipeline evidence, but it may expire. A registry or package repository gives the object a durable coordinate, authentication boundary, metadata, and lifecycle policy.

The failure mode is subtle: teams often publish only latest, rebuild separately for staging and production, or trust a proxy cache as if it proves origin. Those patterns break the chain from tested bytes to deployed bytes.

Core rule: promotion should change a reference or authorization decision, not recreate the artifact. Preserve the producer SHA and verify the same package checksum or container manifest digest at every consumption boundary.

2. Terms before commands

Term Meaning in this chapter Important boundary
Package Registry GitLab-hosted package distribution for npm, Maven, PyPI, NuGet, Generic Packages, and other supported formats. It stores distribution objects; it is not the runner workspace or CI artifact store.
Generic Package A named/versioned collection of arbitrary files published through the package API. Generic duplicate files are allowed by default unless group duplicate settings restrict them.
Container Registry OCI/Docker image distribution attached to a GitLab project. A tag can move; a manifest digest identifies immutable registry content.
Dependency Proxy Group-level pull-through cache for Docker Hub images. Caching improves reliability/rate-limit behavior but does not replace digest pinning or provenance.
Promotion Making already-built bytes eligible for another consumer/environment. Do not rebuild per environment. Reference or copy verified bytes and re-check digest.
Protected package/tag Role-based restriction on publishing/deleting matching names/tags. Protection controls actors; it is not the same as immutable content addressing.
Retention/cleanup Rules that remove old assets/tags to manage storage. Cleanup is lifecycle policy, not a provenance decision.

3. State model: distribution adds a durable identity layer

State layer Evidence to capture Why it matters
Source/revision CI_PIPELINE_SOURCE, ref, CI_COMMIT_SHA, producer pipeline/job IDs Connects distributed bytes to the source and execution that produced them.
Compiled configuration Merged YAML, rules result, image/tool versions Proves which publication/pull jobs GitLab actually created.
Identity/trust Token class, source project, target allowlist/role result; never token value Explains why a push/pull was authorized or denied.
Registry/package Package path/version/file checksum or image repository/tag/manifest digest Names the durable distribution object.
Consumer/promotion Exact URL or @sha256 reference, download/pull digest, target manifest Proves consumers and promoted environments use the same bytes.
Retention/governance Protection rule, duplicate policy, cleanup rule, exact cleanup target Prevents retention from silently destroying release evidence.
External state Runtime/deployment reference to exact package version or image digest Separates registry success from deployment and health.

4. Mental model: build once, publish once, consume by immutable identity

A producer job starts from an exact source SHA and creates bytes. Before publication it computes a checksum. Authentication authorizes publication to one package path or image repository. The registry assigns durable metadata: package name/version/file checksum or image manifest digest. Downstream consumers retrieve that exact identity and verify it. Promotion changes which environment or policy points at the object; it should not invoke a second build.

flowchart LR
  A[Source SHA + producer pipeline] --> B[Build bytes once]
  B --> C[Authenticate to registry]
  C --> D[Publish version/tag]
  D --> E[Record checksum or manifest digest]
  E --> F[Consumer pulls immutable identity]
  F --> G[Promotion reference]
  G --> H[Retention / protected cleanup]
  H --> I[Deployment and health verified separately]

5. Generic Packages: explicit coordinates and stored checksums

A Generic Package file is addressed by project, package name, package version, and file name. GitLab accepts CI_JOB_TOKEN for CI automation and stores checksums for uploaded files. That makes Generic Packages a useful mandatory lab path because it needs no privileged container daemon.

PACKAGE_NAME="glci-ch23-demo"
PACKAGE_VERSION="0.0.0-${CI_PIPELINE_ID}"
FILE="dist/payload.txt"
sha256sum "$FILE" | tee evidence/payload.sha256

curl --fail --location \
  --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
  --upload-file "$FILE" \
  "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${PACKAGE_VERSION}/payload.txt"

6. Container images: tag for humans, digest for machines

A container tag such as 1.4.0 or main is a mutable reference unless stronger controls prevent updates. After push, capture the registry manifest digest and use repository@sha256:… for deployment/promotion evidence. Do not confuse a local Docker image ID with the registry manifest digest.

Identity Can move? Use
image:latest Yes Human convenience only; never sole release evidence.
image:v1.4.0 Potentially, unless policy prevents overwrite Readable release label; verify what digest it resolves to.
image@sha256:… Content-addressed Preferred machine identity for reproducible pull/deploy.

7. Authentication is part of provenance

Within the same project, CI provides CI_REGISTRY_USER/CI_REGISTRY_PASSWORD for container-registry push/pull and CI_JOB_TOKEN for package APIs. For external systems, deploy tokens can use narrow read_registry, write_registry, read_package_registry, or write_package_registry scopes. Cross-project job-token access adds two gates: the target project allowlist and the permissions of the user who started the job.

Secret-handling rule: record the token class and authorization result, never the token value. For Docker, use --password-stdin; do not place credentials in image names, URLs, command-line arguments, or captured logs.

8. Dependency Proxy: availability optimization, not provenance

The Dependency Proxy is group-level and acts as a pull-through cache for Docker Hub images. In CI, GitLab supplies dependency-proxy variables and can authenticate automatically. If a Docker Hub tag moves, the proxy still does not transform that tag into trusted provenance. Reproducible consumption needs a verified upstream digest and appropriate source trust.

Question Dependency Proxy helps? Still required
Reduce repeated upstream pulls Yes Monitor cache/storage and upstream behavior.
Mitigate Docker Hub rate pressure Yes Authenticate and respect upstream terms.
Prove image source integrity No Pin/verify digest and provenance separately.
Store your project image No Use the project Container Registry.

9. Read-only inspection first

printf 'source=%s ref=%s sha=%s pipeline=%s job=%s\n' \
  "$CI_PIPELINE_SOURCE" "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA" \
  "$CI_PIPELINE_ID" "$CI_JOB_ID"

printf 'registry=%s image=%s project=%s\n' \
  "$CI_REGISTRY" "$CI_REGISTRY_IMAGE" "$CI_PROJECT_PATH"

# Inspect package/download coordinates; never dump the token itself.
printf 'package=%s version=%s\n' "$PACKAGE_NAME" "$PACKAGE_VERSION"

10. Common mistakes and safer patterns

Mistake Causal problem Safer pattern
Publish only latest Consumers cannot prove which bytes were used. Publish a unique version and capture digest/checksum.
Rebuild for production Promotion changes bytes instead of authorization/reference. Promote the exact object already tested.
Assume proxy cache = provenance Cache says where bytes were fetched through, not whether they are trusted. Pin digest and verify source/provenance.
Use deploy token everywhere Longer-lived credential expands blast radius. Prefer ephemeral job identity in CI; use deploy token for non-CI integration only.
Apply broad cleanup regex immediately Can erase release evidence or rollback targets. Start narrow, dry-run/inspect, protect important tags/packages, then expand cautiously.

Knowledge check

Why is a container manifest digest stronger evidence than a tag?

What does a Generic Package coordinate need besides the project?

Does a CI job-token allowlist grant project membership?

What problem does the Dependency Proxy solve?

What should promotion change?

Next lesson

Guided hands-on workflow and core operations

Publish one deterministic Generic Package, verify it by exact version/checksum, optionally push a tiny scratch-based container and capture its digest, inspect Dependency Proxy identity, and clean up only bounded lab objects.

Version and compatibility note

GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.

Official references and version notes

Documentation verification date: 2026-09-12. Generic Packages, Package Registry, Container Registry, Dependency Proxy, protected packages, and protected container tags are documented for Free/Premium/Ultimate unless noted otherwise. Current immutable container-tag rules are Ultimate-only. Dependency Proxy is group-level and supports Docker Hub images. Current protected-container-tag rules are Free and protected tags are excluded from container cleanup policies. Container tag immutability is an Ultimate feature, so the chapter teaches digest pinning as the mandatory portable path.

Current assumptions used in this chapter: Version-sensitive YAML, Runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations must be verified against the exact GitLab, GitLab Runner, tool, and external-system versions used in production. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for reproducible work.

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
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.