← ResourcesDEVOPS Β· ARTIFACTS

Artifact management and versioning in delivery

Traceability, signing and reproducibility to know exactly which artifact is running in production.

STRALYA16 min readAugust 2026

Why artifact traceability is critical in an automated pipeline

In a continuous delivery context, dozens or hundreds of artifact versions may be built, tested, and deployed every day. Without clear management of these versions, it becomes impossible to know precisely which code, which configuration, or which dependency produced the behavior observed in production. This opacity leads to blind debugging, slows incident resolution, and creates security risks: how do you verify that a newly discovered vulnerability affects only certain versions in production if you do not know which version runs on which server? Rigorous artifact management addresses three fundamental needs. First, full traceability: every deployed artifact must be linked unambiguously to the source commit, the dependencies used, the tests that validated it, and the target environment. Second, reproducibility: a previous deployment must be replayable identically, source commit and dependencies included, enabling a reliable rollback when a problem arises. Third, compliance and audit: regulations (GDPR, SOC 2, ISO 27001) often require documented proof of who built, validated, and approved each version before it reached production. A pipeline without explicit artifact management is a blind pipeline.

Artifact versioning strategies: semantics and unique identifiers

The first decision is to choose a consistent versioning scheme. Semantic versioning (MAJOR.MINOR.PATCH, e.g. 2.1.3) remains the norm for applications: MAJOR for breaking changes, MINOR for new backward-compatible features, PATCH for bug fixes. However, this scheme alone is not enough in an automated pipeline where builds may be generated on every commit. The recommended practice is to combine semantic versioning with an immutable unique identifier: the source commit hash. For example, a Docker image can be tagged both 2.1.3 (semantic) and 2.1.3-a1f9c24 (with hash), or latest for the most recent development build and 2.1.3-prod for the one validated in production. This dual approach offers human readability ("version 2.1.3") and machine precision ("exactly this code, not another"). Some teams prefer a pure incremental numbering generated by the pipeline itself (build #12847), paired with the commit hash and a timestamp: the advantage is zero ambiguity of interpretation, but any manual version change becomes hard to trace. The choice depends on team culture, but the golden rule remains: every artifact must have at least one globally unique identifier (commit hash or build ID) that distinguishes it from all others, unchangeable and verifiable.

Registries and repositories: centralizing artifacts and their metadata

A centralized artifact registry is the heart of version management. For containers (Docker), that is Amazon ECR, Docker Hub, or a self-hosted private registry. For binaries (e.g. Java JARs, Python wheels), that is Artifactory, Nexus, or S3 with an organized structure. For infrastructure-as-code (Terraform, CloudFormation), that is Git itself or a Terraform Registry repository. What matters is that this registry is not merely passive file storage, but a system that captures and preserves critical metadata: the exact build date and time, the commit author, the full commit hash, the resolved dependency chain (e.g. the exact versions of Python, of the libraries, of the reference database), the unit and integration test results, the security scans (SAST, DAST, dependency vulnerabilities), the cryptographic signatures or checksums used to verify integrity, and finally the complete deployment history (who pulled this version, when, on which environment). AWS ECR offers tags and an image log, allowing you to attach custom metadata via Docker labels or Kubernetes annotations. Artifactory and Nexus provide APIs to enrich each artifact with attributes, build metadata (Build Name and Build Number tied to your CI system), and traceability chains. Most modern pipelines automatically generate a manifest (in JSON or YAML) describing the artifact: source code, transitive dependencies, test results, compliance scans, which can be archived alongside the artifact itself or in an attestation management system (Grafeas, Kyverno). Without this metadata, an artifact is just an opaque file in a repository.

Immutable traceability: linking artifacts, deployments, and production

Once the artifact is built and stored with its metadata, the next step is to establish immutable links between the artifact and each stage of its journey to production. At build time, the pipeline captures and preserves: the exact source commit hash (not just the branch, which can change), the commit author, the modified files, and the complete build logs. At test time, it records: the results of all automated tests (unit, integration, performance), the execution logs, the code coverage metrics, and the security scan reports (SAST, dependencies). At approval time (if applicable), it documents: who approved the deployment, when, and from which system (JIRA ticket, release form, Slack chatbot). At deployment time, it validates: that the artifact pulled from the repository matches exactly what was approved (hash or signature verification), that the environment variables match what was tested, and that the version is indeed the expected one. Finally, once in production, the pipeline records: when the deployment started and finished, on which servers or containers, with which configuration changes, and whether the deployment succeeded or failed. This immutable history is generally stored at three levels. First level: in the artifact system itself (ECR records the date and author of each image). Second level: in the CI/CD system (Jenkins, GitHub Actions, GitLab CI store the build and deployment logs). Third level: in a decentralized audit system (git commit history for the source code, Git Tags for releases, Kubernetes Audit Log for deployments on Kubernetes). The union of these three sources makes it possible, when a problem occurs, to trace the full journey of an artifact from its origin to its presence in production, with no risk of retrospective tampering.

Practical strategies for naming and tagging artifacts

Artifact naming must be readable for a human and parsable by a machine. For Docker images, a common convention is registry.example.com/team/application:version-commit-timestamp. For example, ecr.aws/my-company/api-users:2.1.3-a1f9c24-2024-01-15T10h32. This naming offers several advantages: the registry prefix guarantees global uniqueness, the team and application enable efficient search and sorting, the semantic version is readable at a glance, and the commit hash plus timestamp avoid collisions and allow a precise rollback. However, some teams prefer a more compact naming for daily use (just api-users:2.1.3 or api-users:latest) and use the registry metadata to keep the details (commit hash, author, and so on). In that case, the registry must support queries by metadata (e.g. "find all images created by Alice since yesterday"). For non-containerized binaries (JAR artifacts, Python wheels, and the like), the convention is often groupId/artifactId/version/artifactId-version.jar (Maven), supplemented by Artifactory or Nexus metadata. Git Tags (e.g. release/2.1.3, deployment/prod/2.1.3) add an extra layer of marking in the source repository, linking the code to the deployed version. In every case, what matters is consistency: a team must apply the same scheme to all artifacts, and that scheme must be documented and enforced through the pipeline itself (a developer should never create a version manually; the pipeline does). An additional control can forbid the deployment of any artifact that does not follow this convention.

Authenticating and signing artifacts for security

An artifact is not secure until its provenance and integrity can be verified. Two main mechanisms address this need. First mechanism: source authentication. Every artifact must be created by a controlled, identifiable pipeline process, never by a developer manually on their machine. The pipeline itself must authenticate to the artifact registry (credentials, IAM roles, temporary tokens) to push to it. AWS ECR controls access through IAM policies and scans images on push. Docker Registry supports authentication tokens. Artifactory and Nexus support granular roles and permissions. This authentication leaves a trail: who (which pipeline) created the image and when. Second mechanism: cryptographic signing and integrity verification. An artifact can be signed by the pipeline at creation (e.g. GPG signature of the Docker manifest, COSIGN signature for OCI images, Dockerfile signing with tools such as Notary). On read, before deployment, the deployment system (Kubernetes, ECS, and so on) can verify the signature with a pre-distributed public key, ensuring the artifact has not been altered and truly comes from who claims to have created it. COSIGN (a CNCF tool) offers a modern solution: it signs OCI artifacts with a private key stored securely (e.g. AWS KMS, HashiCorp Vault), and Kubernetes can verify these signatures through admission policies (Kyverno, Portieris) that forbid the deployment of unsigned images. For binaries, the same principles apply: a JAR can be signed with a Maven private key, a Python wheel signed with GPG. Verification then happens at dependency resolution (Maven Verify, pip signature check). Finally, checksums (SHA256 of the artifact content) allow a quick integrity check without heavy cryptographic work, but they do not guarantee authentication. Best practice combines checksums (fast verification) and signatures (authentication). Without these mechanisms, an attacker could in theory replace an artifact in the registry or intercept an image on read, without anyone noticing.

Dependency management and full reproducibility

An artifact does not live in isolation: it depends on a base (e.g. a base Docker image of OS plus runtime), on external dependencies (open-source libraries, SDKs, APIs), and on a configuration (variables, secrets, config files). To guarantee reproducibility, you must pin all these layers. In Docker, that means using immutable base tags, never FROM ubuntu:latest. Prefer FROM ubuntu:22.04 (a named version) or, better still, FROM ubuntu:22.04@sha256:abc123... (a tag pinned to the content hash). In a package.json or a Python requirements.txt, that means listing exact dependency versions, not ranges (pip freeze > requirements.txt produces a complete snapshot). For transitive dependencies (the dependencies of your dependencies), modern tools generate a lockfile (package-lock.json, poetry.lock, go.sum) that records every dependency down to the lowest level with its hash. The pipeline must preserve this lockfile as part of the artifact's metadata: if someone wants to reproduce the artifact months later, they retrieve the source commit, apply the lockfile, and obtain exactly the same dependencies. Terraform works similarly: the .terraform.lock.hcl file records the exact versions of the providers and modules used by a deployment plan. On the configuration side, variables and secrets must not be baked into the artifact (a Docker image compiling secrets is a danger), but injected at deployment through secure mechanisms (Kubernetes Secrets, AWS Secrets Manager, Vault). However, the artifact must document which variables it expects and with which default values it was tested: this metadata must also be preserved. Tools such as SBOM generators (Software Bill of Materials) scan the artifact and produce an exhaustive list of all dependencies, versions, and hashes, which can serve as an attestation and an audit record. An artifact without an SBOM or a preserved lockfile is not reproducible, and therefore not fully traceable.

Monitoring and alerting on versions in production

Once an artifact is in production, responsibility does not end. The pipeline must continuously record which versions run on which servers or pods and alert if an inconsistency appears. Kubernetes makes this easy: each pod has an image annotation, and kubectl get pods -o wide shows the active image. A monitoring system (Prometheus, CloudWatch) can scrape the metadata of pods or EC2 containers (via tags or the EC2 APIs) and build a real-time view of the versions in production. Alerts can trigger if: an unapproved version is in production (e.g. a developer pushed an image directly to ECR without going through the pipeline), an obsolete version (known to be vulnerable, pulled from the registry) is still active, or a mismatch appears between what was expected and what is actually running. Integrate this audit into your DevOps dashboards: show the current version of each service, the commit author, the deployment date, the test results for that version, and the list of known CVEs for it. This real-time feedback enables a quick reaction when a problem occurs: if a critical vulnerability is discovered in a production version, the team instantly sees which services contain it and can decide on an emergency update. It is also valuable feedback for development: knowing that two-month-old code still runs in production can motivate efforts to clean up old branches or deprecate them. Integrations with broader observability tools (distributed tracing, profiling) also let you correlate errors observed in production with a specific version: "the 500 errors of the last 30 minutes are mostly on version 2.0.5, rarely on 2.1.0".

Retention and archiving of obsolete artifacts

Over time, the artifact registry accumulates hundreds or thousands of versions. A retention strategy must balance historical traceability against storage costs. Typical rules are: keep all artifacts of an actively used version (e.g. the last 5 semantic versions), keep all artifacts associated with a major release tag (to be able to reproduce a commercial product released two years ago), and delete development images (e.g. commits on the develop branch, intermediate builds) after 30 days. ECR offers lifecycle policies: "keep images tagged prod-* indefinitely, delete the others after 7 days". Artifactory and Nexus offer even finer granularity. What matters is automating this retention rather than letting it grow without limits. Before deletion, export a manifest (a list of the deleted artifacts, versions, and creation dates) to a long-term archiving system (e.g. S3 Glacier) for historical compliance. For critical artifacts (production versions, decision-relevant versions), consider infinite retention or administrator-supervised deletion. For development or temporary CI artifacts, a short retention reduces complexity and cost. Some teams segment the registry: a development registry with short retention, a staging registry with medium retention, and a production registry with infinite retention. This simplifies governance and reduces accidents.

AWS TEARDOWN Β· FREE

Get the AWS Teardown: where your bill really goes.

The guide listing the 12 cost areas that leak the most at scale-ups, and how to plug them. Free, by email, no obligation.