Artifact Management
Artifacts are the outputs of your CI/CD pipeline — Docker images, compiled binaries, npm packages, Helm charts, and more. Artifact management covers how you store, version, scan, promote, and distribute these outputs reliably.
Types of Artifacts
Section titled “Types of Artifacts”| Artifact Type | What It Is | Where It’s Stored |
|---|---|---|
| Container image | Docker/OCI image | Container registry (GHCR, ECR, ACR, Docker Hub) |
| Package | npm, PyPI, Maven, NuGet package | Package registry (npmjs.com, PyPI, GitHub Packages) |
| Binary | Compiled executable | Object storage (S3) or artifact repo (Artifactory) |
| Helm chart | Kubernetes package | Helm repository or OCI registry |
| Build output | Static site, compiled assets | CI/CD artifact storage or object storage |
| Terraform plan | Saved plan file | CI/CD artifact storage |
Container Registries
Section titled “Container Registries”Container registries store Docker/OCI images — the most common artifact in modern CI/CD.
Registry Options
Section titled “Registry Options”| Registry | Type | Notes |
|---|---|---|
| Docker Hub | Public SaaS | Default registry, free public images, rate limits on free tier |
| GitHub Container Registry (GHCR) | GitHub-native | Free for public repos, integrates with GitHub Actions |
| AWS ECR | AWS-native | Private, integrates with ECS/EKS, lifecycle policies |
| Azure ACR | Azure-native | Private, integrates with AKS, ACR Tasks for builds |
| Google Artifact Registry | GCP-native | Supports Docker, npm, Maven, Python, Go |
| GitLab Container Registry | GitLab-native | Built-in, $CI_REGISTRY variable |
| JFrog Artifactory | Universal (commercial) | Supports all artifact types, enterprise features |
| Harbor | Open source | Self-hosted, RBAC, vulnerability scanning, replication |
For cloud-specific registry details, see AWS ECR and Azure ACR.
Basic Registry Operations
Section titled “Basic Registry Operations”# Docker Hubdocker logindocker build -t myorg/myapp:1.4.0 .docker push myorg/myapp:1.4.0
# GitHub Container Registryecho $GITHUB_TOKEN | docker login ghcr.io -u $GITHUB_ACTOR --password-stdindocker build -t ghcr.io/myorg/myapp:1.4.0 .docker push ghcr.io/myorg/myapp:1.4.0
# AWS ECRaws ecr get-login-password | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.comdocker build -t 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:1.4.0 .docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:1.4.0Image Tagging Strategies
Section titled “Image Tagging Strategies”How you tag images determines how you identify, deploy, and roll back versions.
Common Tagging Schemes
Section titled “Common Tagging Schemes”| Strategy | Example | Pros | Cons |
|---|---|---|---|
| Git SHA | myapp:a1b2c3d | Unique, traceable to exact commit | Not human-readable |
| SemVer | myapp:1.4.0 | Clear versioning, human-readable | Requires release process |
| Branch + SHA | myapp:main-a1b2c3d | Identifies branch and commit | Verbose |
| Build number | myapp:42 | Simple, incrementing | Not traceable to commit |
latest | myapp:latest | Convenient for dev | Ambiguous, breaks reproducibility |
Recommended: SemVer + SHA
Section titled “Recommended: SemVer + SHA”# Tag with both SemVer and Git SHAdocker tag myapp:build ghcr.io/myorg/myapp:1.4.0docker tag myapp:build ghcr.io/myorg/myapp:a1b2c3ddocker tag myapp:build ghcr.io/myorg/myapp:latest
docker push ghcr.io/myorg/myapp:1.4.0docker push ghcr.io/myorg/myapp:a1b2c3ddocker push ghcr.io/myorg/myapp:latest- SemVer for humans and deployment configurations.
- SHA for debugging and exact traceability.
latestfor convenience (but never use in production Kubernetes manifests).
Image Tag Immutability
Section titled “Image Tag Immutability”Enable tag immutability to prevent overwriting existing tags:
# AWS ECR — enable immutabilityaws ecr put-image-tag-mutability \ --repository-name myapp \ --image-tag-mutability IMMUTABLE
# Once myapp:1.4.0 is pushed, it can never be overwrittenThis ensures that myapp:1.4.0 always refers to the exact same image — critical for reproducibility and auditing.
Image Lifecycle Policies
Section titled “Image Lifecycle Policies”Registries accumulate images over time. Lifecycle policies automatically clean up old images:
AWS ECR
Section titled “AWS ECR”{ "rules": [ { "rulePriority": 1, "description": "Keep last 10 tagged images", "selection": { "tagStatus": "tagged", "tagPrefixList": ["v"], "countType": "imageCountMoreThan", "countNumber": 10 }, "action": { "type": "expire" } }, { "rulePriority": 2, "description": "Delete untagged images older than 7 days", "selection": { "tagStatus": "untagged", "countType": "sinceImagePushed", "countUnit": "days", "countNumber": 7 }, "action": { "type": "expire" } } ]}GHCR / Docker Hub
Section titled “GHCR / Docker Hub”Use scheduled GitHub Actions or CLI scripts to prune old images:
# Prune old GHCR images- uses: actions/delete-package-versions@v5 with: package-name: myapp package-type: container min-versions-to-keep: 10 delete-only-untagged-versions: trueVulnerability Scanning
Section titled “Vulnerability Scanning”Scan images for known CVEs before deploying:
Scan in CI
Section titled “Scan in CI”# GitHub Actions — Trivy scan- uses: aquasecurity/trivy-action@master with: image-ref: ghcr.io/myorg/myapp:${{ github.sha }} severity: CRITICAL,HIGH exit-code: 1 # Fail pipeline on findingsRegistry-Native Scanning
Section titled “Registry-Native Scanning”| Registry | Scanning |
|---|---|
| AWS ECR | Basic scanning (Clair) or Enhanced (Inspector) |
| Azure ACR | Defender for Containers |
| Docker Hub | Docker Scout |
| GHCR | Dependabot alerts for container images |
| Harbor | Built-in Trivy scanning |
Scan-on-Push
Section titled “Scan-on-Push”Configure registries to scan images automatically when pushed:
# AWS ECR — enable scan on pushaws ecr put-image-scanning-configuration \ --repository-name myapp \ --image-scanning-configuration scanOnPush=truePackage Registries
Section titled “Package Registries”Beyond containers, CI/CD pipelines publish packages to language-specific registries:
Common Registries
Section titled “Common Registries”| Language | Registry | Publishing |
|---|---|---|
| JavaScript | npm (npmjs.com) | npm publish |
| Python | PyPI (pypi.org) | twine upload or gh-action-pypi-publish |
| Java | Maven Central, GitHub Packages | mvn deploy |
| Go | Go Module Proxy | Git tag (automatic) |
| .NET | NuGet (nuget.org) | dotnet nuget push |
| Rust | crates.io | cargo publish |
| Helm | Helm repository or OCI registry | helm push |
GitHub Packages (Multi-Format)
Section titled “GitHub Packages (Multi-Format)”GitHub Packages supports multiple package types from one place:
# npmnpm publish --registry=https://npm.pkg.github.com
# Docker / OCIdocker push ghcr.io/myorg/myapp:1.4.0
# Mavenmvn deploy -DaltDeploymentRepository=github::default::https://maven.pkg.github.com/myorg/myappArtifact Promotion
Section titled “Artifact Promotion”Promotion is the practice of moving a tested artifact from one stage to the next — instead of rebuilding:
Build: myapp:a1b2c3d ──► push to registry │Test: pull myapp:a1b2c3d ◄───┘ run tests tests pass │Staging: deploy myapp:a1b2c3d ◄──┘ smoke tests pass │Production: deploy myapp:a1b2c3d ◄──┘ (same image, never rebuilt)Why Promote, Not Rebuild?
Section titled “Why Promote, Not Rebuild?”| Practice | Risk |
|---|---|
| Rebuild for each environment | Different builds may produce different binaries (dependency drift, build non-determinism) |
| Promote the same artifact | Exact binary tested in staging is deployed to production — guaranteed consistency |
Promotion Patterns
Section titled “Promotion Patterns”| Pattern | How |
|---|---|
| Tag promotion | Add a tag: myapp:a1b2c3d → also tag as myapp:staging, then myapp:production |
| Registry promotion | Copy image from dev-registry → prod-registry |
| Manifest update | Update the image tag in the deployment manifest (GitOps) |
# Tag promotiondocker tag myapp:a1b2c3d myregistry/myapp:stagingdocker push myregistry/myapp:staging
# Later, after staging validation:docker tag myapp:a1b2c3d myregistry/myapp:productiondocker push myregistry/myapp:productionHelm Chart Repositories
Section titled “Helm Chart Repositories”Helm charts are another key artifact type for Kubernetes deployments:
OCI-Based Helm Registry (Modern)
Section titled “OCI-Based Helm Registry (Modern)”# Push chart to OCI registryhelm push myapp-1.4.0.tgz oci://ghcr.io/myorg/charts
# Pull and installhelm install myapp oci://ghcr.io/myorg/charts/myapp --version 1.4.0Traditional Helm Repository
Section titled “Traditional Helm Repository”# Package and upload to a chart repo (e.g. ChartMuseum, S3, GitHub Pages)helm package myapp/helm repo index . --url https://charts.myorg.com# Upload index.yaml and .tgz to the repoUniversal Artifact Repositories
Section titled “Universal Artifact Repositories”For organizations managing many artifact types, universal repositories consolidate everything:
| Tool | Supports | Type |
|---|---|---|
| JFrog Artifactory | Docker, npm, Maven, PyPI, Helm, Go, NuGet, generic | Commercial |
| Sonatype Nexus | Docker, npm, Maven, PyPI, NuGet, Helm | Open source + commercial |
| GitHub Packages | Docker, npm, Maven, NuGet, Gradle | GitHub-native |
| Google Artifact Registry | Docker, npm, Maven, Python, Go, Apt, Yum | GCP-native |
Best Practices
Section titled “Best Practices”| Practice | Why |
|---|---|
Never use latest in production | Ambiguous, not reproducible |
| Enable tag immutability | Prevent overwriting released versions |
| Scan images on push | Catch vulnerabilities before deployment |
| Use lifecycle policies | Avoid unbounded storage growth |
| Promote, don’t rebuild | Same artifact in staging and production |
| Sign images | Prove provenance (cosign, Notary) |
| Use minimal base images | Fewer packages = fewer vulnerabilities |
| Automate cleanup | Delete untagged images and old versions |
| Multi-architecture images | Build for amd64 and arm64 with docker buildx |
| Store SBOM alongside images | Track components for compliance and incident response |
Key Takeaways
Section titled “Key Takeaways”- Container registries (GHCR, ECR, ACR, Docker Hub) are the primary artifact store in modern CI/CD.
- Tag images with SemVer + Git SHA — human-readable and traceable.
- Enable tag immutability and scan-on-push for security and reproducibility.
- Lifecycle policies automatically clean up old images to control storage costs.
- Promote the same artifact through environments — never rebuild for production.
- Package registries (npm, PyPI, Maven) publish libraries; Helm repos publish Kubernetes charts.
- Use universal repositories (Artifactory, Nexus) if you manage many artifact types.