Chapter 22Lesson 01~300 minutes

Package Registry, Dependency Proxy, Package Formats, Permissions, and Artifact Distribution: Concepts, Architecture, and Mental Model

Model GitLab packages as governed release artifacts with project/group namespace, format-specific endpoints, version identity, authentication, provenance, retention, and dependency-proxy trust boundaries.

Mental modelPackage RegistryGeneric PackagesNamespaceIdentityDependency Proxy

Learning objectives

  • Distinguish Package Registry packages from job artifacts, OCI images, and dependency proxies.
  • Explain project ownership, group aggregation, package format/version identity, and provenance.
  • Choose among documented Generic Package credentials without confusing authentication with authorization.
  • Treat duplicate/immutability semantics as package-format and configuration specific.
  • Explain the different current availability/status of container-image and package dependency proxies.
Availability baseline (verified 2026-08-22 against current GitLab 19.3 documentation). GitLab Package Registry, Generic Packages, CI/CD job-token authentication to the registry, the Packages API, and the container-image Dependency Proxy are available on Free/Premium/Ultimate across GitLab.com, Self-Managed, and Dedicated. The container-image Dependency Proxy is group-scoped, can be disabled by administrators, and currently proxies Docker Hub container images. The newer dependency proxy for packages is Premium/Ultimate and Beta, so it is optional/read-only here. The mandatory labs use one tiny Generic Package in a disposable project and do not require a paid tier, cloud account, private upstream registry, persistent token, or privileged runner.

1. The practical problem: a build file becomes a dependency the moment someone consumes it

Chapter 21 optimized pipeline execution. Chapter 22 changes the lifecycle of the output. A job artifact can be excellent short-lived pipeline evidence, but a reusable library, CLI binary, schema bundle, model asset, or installer needs a stable package identity that consumers can resolve independently of the producing job.

Publishing therefore creates a production dependency. You must know which project owns the package, which endpoint names it, which version consumers request, who may publish or delete it, which credential performed the action, how integrity is verified, and what happens if the version disappears.

2. Four objects that look similar but have different contracts

Object Primary purpose Identity / lifecycle
Job artifact Move or preserve pipeline output. Bound to job/pipeline; retention-oriented; Chapter 16.
Package Registry package Distribute a versioned dependency or release file. Project-owned package name/version plus format-specific metadata/endpoints.
Container Registry image Distribute OCI container images. Repository plus immutable digest and mutable tags; Chapter 23.
Dependency Proxy Cache/proxy an upstream dependency. Cache identity follows upstream reference; it is not the same as publishing your own package.

Do not promote an artifact into a “package” merely by keeping it forever. The Package Registry gives consumers a package-manager/API contract and creates explicit publish/read/delete permissions.

3. Mental model: source → pipeline identity → project package → consumer

Package trust and data flow
flowchart TD
  S[Commit / ref] --> P[Pipeline + job]
  P -->|ephemeral CI_JOB_TOKEN| R[Project Package Registry]
  R --> M[Package metadata: name + version + files + hashes]
  M --> C[Consumer job / package manager]
  C --> V[Checksum + provenance verification]
  U[Upstream registry] --> DP[Dependency Proxy]
  DP --> C

The producer job authenticates to the project registry and publishes a versioned object. GitLab records package/file metadata and checksums. A consumer resolves an exact package identity and independently verifies content. The Dependency Proxy is a separate upstream-cache path: it reduces repeated external downloads but does not turn a mutable upstream tag/version into your own governed package.

4. Package ownership follows GitLab projects; group views aggregate rather than erase ownership

The durable write boundary is normally a project package registry: publishing to project A creates package state owned by project A. GitLab also exposes group-level views/endpoints for discovery or consumption for formats that support them, aggregating packages in the group hierarchy. That does not mean the package has lost its source project or permission boundary.

For the Generic Package Registry used in this chapter, the file URL is explicitly project-scoped:

PUT /projects/:id/packages/generic/:package_name/:package_version/:file_name
GET /projects/:id/packages/generic/:package_name/:package_version/:file_name

Write the project ID/path into your provenance record. A package with the right name and version in the wrong project is still the wrong dependency.

5. Package formats are product capabilities, not one universal protocol

GitLab supports multiple package-manager protocols and a Generic Package API. Current documentation includes long-established formats such as Composer, Conan, Go, Maven, npm, NuGet, PyPI, Generic Packages, and Helm. Support and detailed behavior are version-sensitive, and newer registry capabilities can appear independently.

Format family Why it exists Behavior to verify before standardizing
Generic Arbitrary release files without a native package protocol. Name/version/file rules, duplicate-file setting, SHA-256, retention.
npm / PyPI / Maven / NuGet Native ecosystem dependency resolution. Scope/group endpoints, duplicate/version rules, forwarding, client auth.
Composer / Conan / Go / Helm Native ecosystem distribution. Supported endpoints, authentication protocol, metadata/hash behavior.
Container images OCI image distribution. Use Container Registry, not Package Registry; Chapter 23.

Do not write a company-wide rule such as “GitLab packages are immutable” without checking the exact format. Duplicate semantics differ by protocol and configuration.

6. Package identity has at least five coordinates

For reproducible consumption, record GitLab host, owning project, package name, package version, and file or package-manager coordinate. Add an integrity hash and producing commit/pipeline when traceability matters.

host: gitlab.example.invalid
project: platform/ch22-lab
format: generic
name: ch22-synthetic
version: 0.0.42
file: payload.txt
sha256: <recorded SHA-256>
producer_sha: <40-hex Git commit>
producer_pipeline: <pipeline id>

A human-friendly version is not a checksum. A checksum is not a source commit. Keep both semantic identity and content identity.

7. Authentication is format-specific; authorization still comes from the target registry

For current Generic Packages, official documentation supports personal access tokens with api, project access tokens with api and sufficient role, CI_JOB_TOKEN, and deploy tokens with read_package_registry and/or write_package_registry. Do not assume every package format accepts every token type.

Credential Good fit Security property
CI_JOB_TOKEN Publish/consume from CI/CD. Short-lived and tied to the running job; preferred mandatory path.
Deploy token Non-GitLab external consumer/publisher. Persistent until expiry/revocation; separate read/write package scopes.
Project/PAT Human/API operations that require user/project permissions. Broader identity; avoid when job/deploy token is sufficient.
Group token Only where the specific registry endpoint documents support. Never infer support from another package format.

Publication also needs authorization. Current project permissions normally require Developer or higher to publish and Maintainer/Owner to delete; project visibility and package-registry visibility affect reads.

8. Version “immutability” is a policy you must prove, not assume

Different formats react differently to the same name/version. Current Generic Package behavior allows additional files under an existing name/version by default, and group Owners can control duplicate Generic package publishing. Maven can add files to an existing name/version, while PyPI rejects a duplicate name/version. This is precisely why consumers should use package-manager semantics plus recorded hashes rather than relying on a generic idea of immutability.

For release-grade packages, prefer one-way version publication, prevent duplicate overwrite/addition where the format supports policy controls, and make republishing the same semantic version a visible exception.

9. Generic Packages give us a clean integrity lab

GitLab automatically calculates SHA-256 for Generic Package files. The Packages API exposes file metadata including file_sha256. Newer glab packages download also verifies downloaded Generic Package files against registry checksum metadata by default.

Response-header checksum verification depends on how object storage/direct download is configured, so the lab does not depend on that header. It verifies the downloaded deterministic content locally and shows the API metadata path separately.

10. “Dependency Proxy” currently means two materially different product surfaces

Container-image Dependency Proxy: Free, group-scoped, Docker-Hub-oriented pull-through cache. In CI, predefined variables such as CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX can route image pulls through it. GitLab checks upstream metadata while reusing cached blobs, so a cache hit is not an integrity guarantee for a mutable upstream tag.

Package dependency proxy: currently Premium/Ultimate and Beta. It caches supported upstream packages into project package state and uses Package Registry authorization. Because of its tier/status, this chapter teaches it through documentation/fixtures, not as a required live lab.

11. Read-only inspection before any publish

In a disposable project, first inspect the package feature and existing state:

PROJECT_ID="<disposable-project-id>"

# Existing package records; glab uses your current authenticated session.
glab api "projects/$PROJECT_ID/packages?per_page=100" \
  | jq 'map({id,name,version,package_type,status,created_at})'

# Confirm the repository SHA that a future package should claim.
git rev-parse HEAD

If package operations are disabled for the project, fix the project feature setting before debugging tokens or curl syntax. If existing production-looking packages are present, do not use that project for the lab.

12. DevOps connection: registry writes are governed release actions

A package publication changes what downstream systems can resolve. Treat it like a deployment-quality action: reviewed source, reproducible build, narrow credential, explicit version, checksum/provenance evidence, retention decision, and consumer-impact analysis before deletion. This is the foundation for Chapter 23 container images and later release/security chapters.

Knowledge check

Why is a job artifact not automatically a good package distribution mechanism?

What is the minimum identity you should record for a Generic Package file?

Why can two GitLab package formats behave differently for the same version number?

What credential is preferred for the mandatory CI publish/consume path?

Does a Dependency Proxy guarantee that a mutable upstream tag has never changed?

Summary

You can now separate package distribution from job artifacts and container images, locate package ownership at the correct project/group boundary, choose a documented credential, reason about format-specific duplicate behavior, and treat dependency proxies as cache layers rather than trust anchors.

Official references

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

Next lesson

Guided Hands-On Workflow and Core Operations

Publish one tiny Generic Package with the job token, consume the exact version, verify deterministic content and registry metadata, then remove only the lab package after proving its identity.

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.