Artifact Management

Production engineering guide for artifact management covering patterns, implementation strategies, and operational best practices.

Artifact management is the system of record for everything your builds produce and your runtimes consume. It is the bridge between continuous integration and continuous delivery: if the pipeline builds it but nobody can find it, or if somebody installs the wrong version, the pipeline has failed its primary contract. This page covers the concrete operations of storing, locating, versioning, promoting, and cleaning up artifacts across a typical software delivery lifecycle.

When it matters

You need artifact management when any of the following are true:

If your team has ever asked “which image tag is running in prod?” or “why did this test fail on the latest release?” without a reliable answer, artifact management is the gap.

Storing artifacts

Every artifact lives in a repository of some kind. The protocol varies by material, but the mechanics of pushing and pulling are consistent.

Container images

Docker-derived registries store images addressed by tag or digest. The tag is mutable by default; the digest is immutable.

# Push an image with a tag (mutable)
docker push myregistry.example.com/myapp:v1.2.3

# Push an image identified by digest (immutable)
docker push myregistry.example.com/myapp@sha256:3a7d7c5e4f8b2a1c6d9e0f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1

If you rely on tags and rebuild with the same tag, the registry may or may not overwrite the previous blob depending on configuration. Digests eliminate this ambiguity entirely.

# Pull by digest (guaranteed immutable)
docker pull myregistry.example.com/myapp@sha256:3a7d7c5e4f8b2a1c6d9e0f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1

Language-specific package repositories

Each language ecosystem has its own registry with its own push/pull surface.

# npm — publish a package version
npm publish

# After publishing, install the exact version
npm install [email protected]

# Maven — deploy a snapshot or release
mvn deploy -DaltDeploymentRepository=snapshots::default::http://repo.example.com/content/repositories/snapshots

# Install a specific version from a remote repository
mvn install:install-file -DgroupId=com.example -DartifactId=myapp -Dversion=1.2.3 -Dfile=target/myapp-1.2.3.jar

Git-based artifacts

Binary assets that belong in version control are handled via Git LFS or by checking compressed blobs into the repository.

# Git LFS — track large files
git lfs track "*.psd"

# Git LFS — push large file content to the remote
git lfs push --all

# Without LFS — add a binary as a gitblob (not recommended for large files)
git add largefile.bin
git commit -m "add large binary"
git push

Versioning and immutability

The central tension in artifact management is between human-friendly labels (tags) and machine-friendly proofs of identity (digests).

When a system supports both, the pattern is to push with a tag for operational convenience, then record the resulting digest for audit and downstream consumption.

# Build, tag, and push; then extract the digest
docker build -t myapp:v1.2.3 .
docker push myregistry.example.com/myapp:v1.2.3
docker inspect myregistry.example.com/myapp:v1.2.3 --format='{{index .RepoDigests 0}}'

The output will be something like myregistry.example.com/myapp@sha256:3a7d7c5e4f8b2a1c6d9e0f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1. That digest is the immutable handle you pass to downstream systems.

Promoting artifacts between environments

Promotion is the act of taking an artifact verified in one environment and making it available in another. The method depends on the registry type.

Container image promotion

Most container registries do not provide a built-in “promote” command. The pattern is pull-by-digest and re-push.

# From production-like to staging
docker pull myregistry.example.com/myapp@sha256:3a7d7c5e4f8b2a1c6d9e0f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1
docker tag myregistry.example.com/myapp@sha256:3a7d7c5e4f8b2a1c6d9e0f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1 staging-registry.example.com/myapp:v2024-06-15
docker push staging-registry.example.com/myapp:v2024-06-15

Some managed registries (Artifactory, Harbor, GitLab Container Registry) offer a copy or move API, but the underlying action is always pull-by-digest then push.

Language package promotion

Promotion in package repositories is typically a matter of publishing to a target namespace or changing the repository configuration.

# npm — publish to a scoped registry
npm config set @myorg:registry https://npm.pkg.example.com/
npm publish --registry https://npm.pkg.example.com/

# Maven — deploy to a different repository ID
mvn deploy -DaltDeploymentRepository=release-repo::default::http://repo.example.com/content/repositories/releases

The key is that the same binary bits end up in a different repository endpoint, identified by the same version string or digest.

Retrieving and consuming artifacts

Consumption is the flip side of storage. The reader should be able to articulate exactly how their runtime fetches the correct artifact.

Container runtimes

Container orchestrators pull images by tag or digest. The orchestration platform may rewrite tags, but if digests are used, the pull is resolved to a specific blob.

# Kubernetes pod spec — pinned by digest
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: myapp
    image: myregistry.example.com/myapp@sha256:3a7d7c5e4f8b2a1c6d9e0f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1
    imagePullPolicy: IfNotPresent

Using the digest form prevents the pod from being silently rescheduled to a new image if the tag is updated elsewhere.

Package managers

# Python — install from PyPI or a mirror
pip install mypackage==2.4.1

# From a specific index URL
pip install --index-url https://pypi.example.com/simple/ mypackage==2.4.1

# Go — fetch a module version
go get github.com/example/[email protected]

# From a private mirror
GOFLAGS=-mod=mod go mod download github.com/example/[email protected]

The version string is the address. If the repository removes or renames that version, the build fails. This is the “sharp edge” of package consumption: version availability is not guaranteed after publication.

Cleanup and retention

Artifacts accumulate. Registries have finite storage, and old versions serve no purpose beyond audit or rollback. Cleanup strategies vary by tool.

Docker registry garbage collection

The Docker Distribution API includes an endpoint for garbage collection, but it is often exposed only through the registry’s administrative UI or a separate gc job.

# Trigger GC via the registry API (if authenticated)
curl -X POST -u admin:password https://registry.example.com/v2/_garbage_collection

The registry will delete manifests and blobs not referenced by any tag or digest. This is irreversible.

Package repository retention

Most package repositories have a retention policy configured at the admin level, not via the CLI.

When retention is enforced by the repository, the CLI may return 404 Not Found for expired versions.

# Attempting to pull an expired snapshot from Artifactory
curl -u user:token https://artifactory.example.com/artifactory/libs-snapshot-local/com/example/app/2.0.0-SNAPSHOT/maven-metadata.xml
# Returns 404 if the snapshot exceeds the retention window

Git LFS retention

Git LFS does not garbage-collect server-side unless the Git host provides the feature (GitHub, GitLab, Bitbucket). Self-hosted LFS requires manual pruning.

# GitHub Actions — cleanup of LFS objects older than 90 days
# (via repository settings, not a CLI command)

On self-hosted LFS servers, expired objects accumulate until disk pressure triggers manual housekeeping.

Sharp edges

Tag vs. digest confusion

The most common failure mode is assuming a tag is immutable. Teams often write deployment scripts that docker pull myapp:latest, expecting the same image, only to find the pod restarts with a new build because another team overwrote the latest tag. The fix is to always resolve to a digest in automation and treat tags as promotional labels only.

Authentication expiry

Registry credentials are frequently stored in ~/.docker/config.json or Kubernetes secrets. These tokens have lifetimes. A pipeline that worked yesterday may fail with 401 UNAUTHORIZED: authentication required at 3 AM when the token expires and the renewal step is missing from the cron job. The error message is identical whether the token expired or the registry requires re-registration; diagnosing which requires checking the token’s expiry field or the registry’s auth log.

Promotion drift

When promotion is performed by pull-by-digest-and-push, a race condition can occur if the source tag is updated between the pull and push. The artifact pushed may no longer match the tag that was intended to be promoted. The pattern to avoid this is to pin the exact digest at the point of promotion and never re-resolve the tag downstream.

# Anti-pattern: resolving tag after pull
docker pull myregistry:latest
docker tag myregistry:latest staging-registry:latest
docker push staging-registry:latest

# Pattern: resolve digest once, use that digest everywhere
DIGEST=$(docker inspect myregistry:latest --format='{{index .RepoDigests 0}}')
docker pull myregistry@${DIGEST#*/}
docker tag myregistry@${DIGEST#*/} staging-registry:v2024-06-15
docker push staging-registry:v2024-06-15

Cleanup without backup

Garbage collection in registries deletes unreferenced blobs. If a team relies on a digest that has been GC’d, docker pull returns manifest unknown: manifest not found in repository. There is no “undelete” within the registry; the only recourse is to rebuild and re-push. This is why many organizations retain a read-only mirror or an immutable backup of critical digests before running GC.

Version deletion by repository policy

Package repositories may delete versions that violate policy (e.g., malicious content, license violations). The error from the client is typically 403 Forbidden or 404 Not Found with no distinction in the HTTP body as to why. The consumer must check the repository’s admin documentation or audit logs to determine if the version was removed by policy or simply never published.

Summary

Artifact management is the operational discipline of giving every build a stable, addressable identity and ensuring that identity can be reliably resolved at consumption time. The concrete operations—docker push/pull by digest, npm publish/install, mvn deploy/install, git LFS push/pull—are well‑understood, but the failure modes tag mutability, credential expiry, GC side‑effects, and promotion drift are where most incidents originate. Pin to digests in automation, treat tags as promotional only, and retain digests of anything that must be reproducible or auditable.

This page was rewritten on 15 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.