Why this matters
As a Platform Engineer, you make builds reproducible and deployments predictable. Artifact repositories store build outputs (images, packages, charts) so pipelines and environments can fetch exactly the same thing every time. Good versioning prevents overwrites, enables rollbacks, and supports safe promotions across environments.
- Keep production deploys stable by pinning exact artifact digests.
- Speed up builds by caching dependencies via proxy repositories.
- Enable compliance with retention, immutability, and provenance (who built what, when, and how).
Concept explained simply
An artifact is the output of a build: a Docker image, JAR, wheel, npm package, Helm chart, or a zipped binary. An artifact repository is a specialized storage that understands formats (Docker/OCI, Maven, npm, PyPI, Helm, NuGet) and enforces policies (immutability, permissions, retention). Versioning is how you label builds so anyone can fetch the exact intended one.
Mental model
Think of a library. Shelves are repositories, books are artifacts, call numbers are versions. If two books share a call number, chaos ensues. Your job is to give each artifact an immutable, traceable call number and place it on the right shelf so others can reliably find it.
SemVer, CalVer, and Git-commit versions
- SemVer: MAJOR.MINOR.PATCH (e.g., 2.5.1). Increase MAJOR for breaking changes, MINOR for features, PATCH for fixes.
- Pre-releases: 2.6.0-rc.1, 2.6.0-beta.2 (lower precedence than stable release).
- Build metadata: 2.6.0+build.57 (informational; doesn’t change precedence).
- CalVer: 2024.10.3 (year.month.patch) – predictable time-based versions.
- Git-based: 1.4.2+gabcdef or 2024.10.3-gabcdef – adds traceability to a commit.
Core practices for artifact repos
- Immutability: Never overwrite a published version. Avoid mutable tags like latest in production.
- Pinning: Deploy by digest (e.g., sha256:...) or exact version, not floating tags.
- Clear coordinates: Use consistent naming conventions (group/project/service) and repo format per ecosystem.
- Promotion model: Promote artifacts between environments via copy, move, or labeling instead of rebuilding.
- Retention & cleanup: Keep recent N versions, protect promoted ones, expire unused after a grace period.
- Access control: CI has write access to a staging repo; release system promotes; runtime has read-only.
- Provenance: Attach labels/metadata (git SHA, build ID, SBOM, signatures) for traceability.
- Caching: Use proxy repos for upstream dependencies to speed builds and increase reliability.
Worked examples
Example 1: Docker image with SemVer + digest pinning
- Build:
docker build -t registry.example.com/payments/api:1.8.0 -t registry.example.com/payments/api:1.8.0-gabc123 . - Push:
docker push registry.example.com/payments/api:1.8.0 docker push registry.example.com/payments/api:1.8.0-gabc123 - Get digest:
docker pull registry.example.com/payments/api:1.8.0 DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' registry.example.com/payments/api:1.8.0) - Deploy by digest (Kubernetes snippet):
image: registry.example.com/payments/api@sha256:... # exact immutable reference
Why this works
SemVer makes intent clear; git hash adds traceability; digest pinning guarantees the exact bytes run in production.
Example 2: JVM service with Maven release/promotion
- Set version for release:
mvn versions:set -DnewVersion=2.4.0 - Package and publish to staging:
mvn -B -DskipTests package deploy -Pstaging - Promote to prod repository (no rebuild):
# via repository manager UI/API, copy from libs-staging to libs-release - Consumers depend on exact version:
<dependency> <groupId>com.example</groupId> <artifactId>orders-service</artifactId> <version>2.4.0</version> </dependency>
Why this works
No rebuild between environments avoids drift. Exact versions ensure reproducibility and safe rollback.
Example 3: Internal npm package with pre-releases and dist-tags
- Bump pre-release:
npm version 3.2.0-rc.1 --no-git-tag-version - Publish to registry with rc tag:
npm publish --tag rc - After validation, publish stable:
npm version 3.2.0 --no-git-tag-version npm publish --tag latest - Consumers opt-in:
# testing uses rc npm install @example/ui@rc # production uses latest npm install @example/ui@latest
Why this works
Dist-tags separate testing and production channels while keeping versions immutable.
Who this is for
- Platform and DevOps engineers setting up CI/CD pipelines and repos.
- Backend engineers publishing services or libraries internally.
- SREs who need repeatable rollouts and quick rollbacks.
Prerequisites
- Comfort with Git basics (commits, tags).
- Understanding of your build tool (Docker, Maven/Gradle, npm, or similar).
- Access to an artifact repository manager or container registry.
Learning path
- Learn artifact types you produce (images, packages, charts).
- Pick a versioning scheme (SemVer or CalVer) and write it down.
- Implement immutable publishing in CI (no overwrites).
- Adopt promotion flows (dev → staging → prod) without rebuilding.
- Add pinning by digest/version in deployment specs.
- Introduce retention, access control, SBOM/signing.
Exercises
Do these hands-on tasks. They mirror the exercises listed below and help you cement the concepts.
Exercise 1: Design your versioning and repo layout
- Choose SemVer or CalVer and define when each part changes.
- Decide how you include commit SHA or build number (metadata or pre-release).
- Define repository coordinates for one service and one shared library.
- Write your promotion policy (what moves from staging to prod, when, and how).
Checklist before you submit
- Versions are immutable and monotonic.
- No environment-specific rebuilds.
- Coordinates follow a consistent naming convention.
- Rollbacks possible by exact version/digest.
Exercise 2: Publish and pin a Docker image
- Build and tag with SemVer and short SHA:
docker build -t repo/app/api:1.2.0 -t repo/app/api:1.2.0-gabc123 . - Push both tags:
docker push repo/app/api:1.2.0 docker push repo/app/api:1.2.0-gabc123 - Capture the digest and write a deployment snippet referencing it.
Checklist before you submit
- Both tags exist in the registry.
- Digest recorded (sha256:...).
- Deployment uses image@sha256 form.
Common mistakes and self-checks
- Using latest in production. Self-check: Your production manifests reference exact versions or digests only.
- Overwriting the same tag. Self-check: Try to republish the same version; it should fail by policy.
- Rebuilding on promotion. Self-check: Check image digest or checksum; it must not change across environments.
- Unbounded artifact growth. Self-check: Do you have retention rules and protected releases?
- Mixing formats in one repo. Self-check: Each ecosystem uses its dedicated repository type.
- Missing provenance. Self-check: Artifacts have labels/metadata with commit, build, and SBOM/signature.
Practical projects
- Set up a three-repo promotion path for Docker (dev, staging, prod) and automate copy/promotion via CI.
- Create a Maven library publishing pipeline with release and pre-release profiles, enforcing immutability.
- Implement npm dist-tags (rc, beta, latest) and a policy that production only consumes latest.
- Add a retention policy that keeps last 20 versions, protects promoted ones, and expires the rest.
Mini challenge
You discover a production deployment referencing a mutable tag. In one paragraph, propose a fix that prevents future drift, including: (1) how to pin, (2) how to promote without rebuilding, and (3) how to enforce it in CI/repo policy.
Next steps
- Add artifact signing and verification in CI/CD.
- Generate and store SBOMs with your artifacts.
- Expand promotion gates with automated tests and approvals.
Quick Test
Take the short test below to check your understanding. Everyone can take it for free. Note: progress is saved only for logged-in users.