DEV Community

Cover image for Artifacts & Registries: Where Your Builds Live
Sri Balaji
Sri Balaji

Posted on • Originally published at thesimplifiedtech.com

Artifacts & Registries: Where Your Builds Live

TL;DR: Learn what your pipeline actually produces and where it lives: immutable build artifacts, meaningful version tags (and why latest is a trap), how registries work, and the build-once-promote-many pattern.

Contents

Your pipeline builds something. Where does it go?

You've got a pipeline that builds and tests your code. But "builds" produces a thing, and that thing has to live somewhere between being built and being deployed. That thing is a build artifact, and the place it lives is a registry. They're quietly central to how every team ships software, and they're the part beginners tend to hand-wave past.

Get this right and your deploys become trustworthy and repeatable. Get it wrong, especially the versioning, and you end up unable to answer the most basic question during an incident: "what is actually running in production right now?" This article makes sure you can always answer it.

📌 Who this is for: Junior engineers who've built a CI pipeline but treat the output as a black box. We focus on container images (the most common artifact today) but the principles, immutability, versioning, promote-don't-rebuild, apply to any artifact: a JAR, a zip, an npm package.

What is a build artifact?

A build artifact is the packaged, ready-to-run output of your build, a single, self-contained unit that can be stored, versioned, and deployed without rebuilding from source.

Your source code isn't directly runnable in production, it has to be compiled, bundled, and packaged first. The result of that packaging is the artifact. For a containerized app it's a Docker image; for a Java app a .jar; for a serverless function a .zip. Whatever the format, the idea is the same: one frozen, deployable bundle.

In the real world In tech
🥕 Raw ingredients Source code
👨‍🍳 Cooking The build step
🍱 A sealed, labeled meal The build artifact
🏷️ The expiry/batch label The version tag
🧊 The fridge it's stored in The registry

Source code is ingredients; the artifact is the sealed, labeled meal.

Immutability: the property that makes it trustworthy

The single most important property of a good artifact is immutability, once built, it never changes. You don't patch it, edit it, or rebuild it in place. If you need a change, you build a new artifact with a new version. The old one stays exactly as it was, forever.

Why does this matter so much? Because immutability is what lets you reason about your system. If version v1.4.2 is running in production and you can pull that exact same v1.4.2 onto your laptop, you can debug precisely what users are hitting. If artifacts were mutable, if "the build" could quietly change underneath you, that guarantee evaporates, and you're back to "works on my machine." Immutability is also what makes rollback trivial: just redeploy the previous artifact, byte-for-byte.

💡 Tip: Immutability is the same reason "build once, deploy many" works. The artifact you tested in staging is provably the artifact you ship to prod, because it literally cannot have changed in between. Mutable artifacts break that proof.

Versioning and tags, and why latest is a trap

An artifact without a meaningful version is nearly useless, you can't tell which is which. Every artifact gets a tag (a label) that identifies it. The quality of your tagging strategy directly determines whether you can answer "what's running in prod?"

Docker's default tag, latest, is the trap almost everyone falls into. latest doesn't mean "the newest", it's just a label that moves to whatever was pushed most recently. It points at different images over time, which destroys the one thing tags are for: knowing exactly what you have.

latest Immutable tag (SHA / semver)
Points to Whatever was pushed last One specific, fixed image
Reproducible deploy? No, meaning drifts Yes, always the same image
Can you roll back to it? No, it already moved Yes, it never moves
"What's in prod?" Unanswerable The exact tag, every time

Why latest fails at the one job a tag has.

Use tags that pin. Two strategies, often combined: the Git commit SHA (e.g. app:9f2a1c7) ties an image to the exact code that built it, perfect for traceability, and semantic versioning (e.g. app:1.4.2) communicates intent to humans. A common pattern is to push both: the SHA for machines, the semver for releases.

⚠️ The latest 2am story: An incident hits. You check production: it's running myapp:latest. Which commit is that? Nobody knows, latest has been re-pushed a dozen times since. You can't reproduce it, can't roll back cleanly, and can't even be sure two servers are running the same image. This is why teams ban latest in production deploys.

What a registry is and how it works

A registry is the storage service where artifacts live, a versioned warehouse your CI pushes to and your deploy targets pull from. For container images, common registries are Docker Hub, GitHub Container Registry (GHCR), AWS ECR, Google Artifact Registry, and Azure ACR. They all speak the same protocol; the workflow is identical.

The flow is a simple loop, and it's the connective tissue between your pipeline and your running app:

  1. CI builds the artifact: Your pipeline builds the image and tags it with the commit SHA (and maybe a version).
  2. CI pushes to the registry: The tagged image is uploaded to the registry, now it's stored, versioned, and shareable.
  3. Deploy pulls from the registry: Staging pulls that exact tag and runs it. After verification, prod pulls the same tag.
  4. Rollback re-pulls an old tag: Need to revert? Re-deploy a previous tag from the registry. It's still there, unchanged.

💡 Tip: Registries are also a security boundary. Use a private registry for anything proprietary, scan images for vulnerabilities on push (most registries do this), and lock down who can push. A poisoned image in a registry deploys straight to production.

Build, tag, and push for real

Here's the whole loop in actual commands. This is what your CI runs under the hood; running it by hand once makes it click.

build-and-push.sh

# Use the Git commit SHA as an immutable tag
TAG=$(git rev-parse --short HEAD)      # e.g. 9f2a1c7
REGISTRY=ghcr.io/your-org
IMAGE=$REGISTRY/myapp

# 1. Build, tagging with the immutable SHA
docker build -t $IMAGE:$TAG .

# 2. Also tag it as a human-readable version (optional)
docker tag $IMAGE:$TAG $IMAGE:1.4.2

# 3. Log in to the registry (token from a secret, never hardcoded)
echo "$REGISTRY_TOKEN" | docker login ghcr.io -u your-user --password-stdin

# 4. Push BOTH tags, they point at the same image bytes
docker push $IMAGE:$TAG
docker push $IMAGE:1.4.2
Enter fullscreen mode Exit fullscreen mode

Note that docker tag doesn't copy anything, it just adds a second name pointing at the same underlying image, so pushing both tags is cheap. Later, your deploy step pulls the specific tag and runs it:

deploy.sh

# Pull and run the EXACT immutable tag, never `latest`
docker pull ghcr.io/your-org/myapp:9f2a1c7
docker run -d -p 3000:3000 ghcr.io/your-org/myapp:9f2a1c7

# Confirm exactly what's running (answers "what's in prod?")
docker ps --format 'table {{.Image}}\t{{.Status}}'
Enter fullscreen mode Exit fullscreen mode

Because the tag is the commit SHA, that last command doesn't just tell you the image is running, it tells you the exact line of source code in production. That traceability, from a running container all the way back to a Git commit, is the entire point.

Build once, promote many

This is the pattern everything in this article serves. You build the artifact once, push it to the registry, and then promote that same artifact through your environments, you never rebuild for staging and rebuild again for prod.

It works precisely because artifacts are immutable and registries store them: staging pulls myapp:9f2a1c7, you verify it, and then prod pulls the byte-identical myapp:9f2a1c7. The thing you tested is, provably, the thing you shipped. Rebuilding per environment throws that guarantee away, a dependency could shift between builds and you'd ship something subtly different from what you tested. We cover this from the pipeline angle in CI/CD Fundamentals.

⚠️ Warning: If your deploy step contains a docker build for production, you're rebuilding, not promoting, and you've lost the guarantee that prod matches what you tested. Production deploys should only ever pull an existing, tested tag from the registry.

Common mistakes that cost hours

  1. Deploying latest to production. It's a moving label, not a version. You can't reproduce it, roll back to it, or even know what it is. Ban it from prod deploys.
  2. Treating artifacts as mutable. Never patch a built artifact in place. Build a new version. Immutability is what makes rollback and debugging possible.
  3. Rebuilding per environment. Build once, push, promote the same tag. A docker build in your prod deploy means prod isn't what you tested.
  4. No traceability from artifact to commit. Tag with the commit SHA so you can always trace a running container back to its exact source.
  5. Hardcoding registry credentials. Registry tokens are secrets, inject them from your CI's secret store and pipe to --password-stdin, never put them in a script or Git.
  6. Never cleaning up old tags. Registries fill up and cost money. Set a retention policy, but keep enough history that you can always roll back.

Takeaways

The whole article in seven lines

  • An artifact is the packaged, ready-to-run output of your build (often a container image).
  • Immutability is the key property: once built, it never changes, you build a new version instead.
  • Tag with something that pins: the Git commit SHA and/or a semantic version.
  • latest is a trap, it moves over time, so it can't reproduce, roll back, or identify a build.
  • A registry stores artifacts: CI pushes, deploys pull, rollback re-pulls an old tag.
  • Tag with the commit SHA for full traceability from a running container back to source.
  • Build once, push, and promote the same tag through every environment, never rebuild for prod.

Where to go next

You know what artifacts are and where they live. Next, make sure the images you push are small and secure, see how this fits the wider pipeline, and practice the commands hands-on.


Originally published on TheSimplifiedTech, where this guide is interactive, with in-browser terminal labs and diagrams. Learn cloud and DevOps by doing, no videos.

Top comments (0)