Artifact Management#

Artifacts — container images, packages, binaries — are the output of your CI pipeline and the input to your CD pipeline. Managing them well means consistent tagging, secure storage, vulnerability scanning, and retention policies.


Container Registries#

A container registry stores and distributes Docker/OCI images. Choose based on your ecosystem:

RegistryBest ForAuthenticationKey Features
Amazon ECRAWS-native workloadsIAM rolesLifecycle policies, image scanning, cross-region replication
Docker HubOpen-source, public imagesUsername/tokenOfficial images, automated builds, rate limits on free tier
GitLab Container RegistryGitLab CI usersCI_JOB_TOKENBuilt-in, zero config, per-project isolation
GitHub Container Registry (ghcr.io)GitHub Actions usersGITHUB_TOKENFree for public images, org-level packages
Google Artifact RegistryGCP workloadsService accountsMulti-format (Docker, npm, Maven, Python)
Azure Container RegistryAzure/AKS usersManaged IdentityGeo-replication, ACR Tasks for in-registry builds

ECR Lifecycle Policy Example#

Automatically clean old images to control storage costs:

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "Keep last 20 tagged images",
      "selection": {
        "tagStatus": "tagged",
        "tagPrefixList": ["v"],
        "countType": "imageCountMoreThan",
        "countNumber": 20
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 2,
      "description": "Remove untagged images after 7 days",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 7
      },
      "action": { "type": "expire" }
    }
  ]
}

Artifact Repositories (Non-Container)#

For JARs, npm packages, Python wheels, and generic binaries:

ToolSupported FormatsDeploymentLicence
JFrog ArtifactoryAll (Maven, npm, PyPI, Docker, Helm, Go, etc.)Cloud or self-hostedCommercial
Sonatype NexusMaven, npm, PyPI, Docker, NuGet, HelmSelf-hosted (OSS) or ProOSS + Commercial
GitHub Packagesnpm, Maven, NuGet, Docker, RubyGemsCloud (GitHub-integrated)Free tier + paid
GitLab Package RegistryMaven, npm, PyPI, NuGet, Go, ComposerBuilt into GitLabFree
AWS CodeArtifactMaven, npm, PyPI, NuGetAWS-managedPay-per-use

When to Use What#

  • Small team, single language → GitLab/GitHub built-in package registry
  • Multi-language monorepo or enterprise → Artifactory or Nexus (central proxy + cache)
  • AWS-native, need upstream proxy → CodeArtifact (proxies npmjs, PyPI, Maven Central)

Semantic Versioning in Pipelines#

Every artifact should carry a meaningful version. SemVer (MAJOR.MINOR.PATCH) is the standard:

SegmentWhen to IncrementExample
MAJORBreaking API/interface changes1.2.3 → 2.0.0
MINORNew features, backward-compatible1.2.3 → 1.3.0
PATCHBug fixes, no API changes1.2.3 → 1.2.4

Automating Version Bumps#

Tools that derive the next version from commit messages (Conventional Commits):

# semantic-release (Node.js ecosystem)
npx semantic-release

# commitizen + standard-version
npx standard-version

# Python: python-semantic-release
semantic-release publish

Conventional Commits → version mapping:

Commit PrefixVersion Bump
fix:PATCH
feat:MINOR
feat!: or BREAKING CHANGE:MAJOR
chore:, docs:, ci:No bump

Image Tagging Strategies#

StrategyFormatProsCons
Git SHAmyapp:a1b2c3dUnique, traceable to commitNot human-readable
SemVer tagmyapp:1.4.2Clear versioningRequires tagging discipline
Branch + SHAmyapp:main-a1b2c3dShows source branchCluttered
Latestmyapp:latestSimpleNon-deterministic, breaks reproducibility
Timestampmyapp:20260803-141200SortableNo semantic meaning
Combinedmyapp:1.4.2-a1b2c3dVersion + traceabilityLonger tag
# GitLab CI example
variables:
  IMAGE_TAG: "${CI_COMMIT_TAG:-${CI_COMMIT_SHORT_SHA}}"
  IMAGE: "${CI_REGISTRY_IMAGE}:${IMAGE_TAG}"

build:
  script:
    - docker build -t $IMAGE .
    - docker push $IMAGE
    # Also tag as latest for convenience (non-prod only)
    - docker tag $IMAGE ${CI_REGISTRY_IMAGE}:latest
    - docker push ${CI_REGISTRY_IMAGE}:latest

Immutable Tags#

Production images should use immutable tags — once pushed, a tag cannot be overwritten:

# ECR: enable image tag immutability
aws ecr put-image-tag-mutability \
  --repository-name myapp \
  --image-tag-mutability IMMUTABLE

Vulnerability Scanning in CI#

Scan images and dependencies before they reach production:

Scanning Tools#

ToolScansIntegrationLicence
TrivyContainer images, filesystem, IaCCLI, CI pluginsOSS (Aqua)
GrypeContainer images, SBOMsCLI, GitHub ActionsOSS (Anchore)
SnykContainers, code, IaC, depsCI/CD, IDE pluginsFreemium
Docker ScoutDocker imagesDocker CLI, HubBuilt into Docker
ECR ScanningECR images (basic or enhanced)AWS-nativeIncluded with ECR

Trivy in CI Pipeline#

# GitLab CI
scan:
  stage: security
  image: aquasec/trivy:latest
  script:
    - trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE
    - trivy image --format json --output trivy-report.json $IMAGE
  artifacts:
    paths:
      - trivy-report.json
  allow_failure: false
# GitHub Actions
- name: Trivy vulnerability scan
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ${{ env.IMAGE }}
    format: 'sarif'
    output: 'trivy-results.sarif'
    severity: 'HIGH,CRITICAL'
    exit-code: '1'

- name: Upload scan results
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: 'trivy-results.sarif'

Gate Policy#

SeverityAction
CriticalBlock deployment, require immediate fix
HighBlock deployment to production
MediumWarn, create ticket, allow deployment
LowReport only

Shifting Left: Dependency Scanning#

Scan dependencies before building the image:

# Python
pip-audit --requirement requirements.txt

# Node.js
npm audit --audit-level=high

# Go
govulncheck ./...

Retention & Cleanup Policies#

Unmanaged registries grow indefinitely. Define policies:

RuleExample
Keep last N tagged images20 per repository
Delete untagged after N days7 days
Keep all release tags (vX.Y.Z)Indefinitely
Delete feature-branch images after mergeSame day
Archive images older than 90 daysMove to cold storage or delete

Key Takeaways#

  1. Use your platform’s native registry for simplicity (ECR for AWS, GitLab Registry for GitLab CI) — add Artifactory/Nexus only when you need cross-platform proxying.
  2. Tag images with SemVer + SHA for both human readability and commit traceability. Never deploy latest to production.
  3. Enable immutable tags in production registries to prevent accidental overwrites.
  4. Scan every image in CI — fail the pipeline on Critical/High findings. Use Trivy or Grype as a free baseline.
  5. Automate cleanup — lifecycle policies prevent registry bloat and reduce storage costs.
  6. Automate version bumps from Conventional Commits — removes human error from the versioning process.