Registries
You’ve built an image from a Dockerfile. It sits on your machine. But production runs on other machines, and so does CI, and so does your teammate’s laptop. They all need the same image. A registry is the shared store images are pushed to and pulled from — the distribution layer that makes “build once, run everywhere” actually reach everywhere.
If a Dockerfile is the recipe and an image is the dish, a registry is the fridge everyone shares. Docker Hub, GitHub Container Registry (GHCR), Amazon ECR, Google Artifact Registry, and a self-hosted Harbor are all registries.
Push and pull
Section titled “Push and pull”The two core operations. Push uploads your image to the registry; pull downloads it. Because images are made of content-addressed layers, only layers the registry doesn’t already have are transferred — pushes and pulls move just the diff, not the whole image every time.
# Name (tag) the image for a registry, then push itdocker build -t ghcr.io/acme/snip:1.4.2 .docker push ghcr.io/acme/snip:1.4.2
# On another machine — pull and run itdocker pull ghcr.io/acme/snip:1.4.2docker run ghcr.io/acme/snip:1.4.2The name encodes where the image lives: ghcr.io/acme/snip:1.4.2 is registry / namespace / repository : tag. Omit the registry host and most tools default to Docker Hub — which is why docker pull nginx
silently means docker.io/library/nginx:latest.
Tags vs digests — the heart of this page
Section titled “Tags vs digests — the heart of this page”This builds directly on the distinction from images & layers, because it’s where it bites in production.
A tag (snip:1.4.2, snip:latest) is a human-friendly, mutable label. It’s a pointer. Nothing
stops someone from pushing different image content under the same tag tomorrow — the tag stays
1.4.2, the bytes underneath change.
A digest (snip@sha256:9c1f…) is the cryptographic hash of the image content. It is immutable.
It doesn’t point at the image — it is a fingerprint of the exact bytes. Pull by digest and you are
guaranteed those exact bytes or an error; there is no “someone moved it.”
TAG → mutable pointer DIGEST → immutable fingerprint ┌──────────────┐ ┌────────────────────────────┐ │ snip:1.4.2 │ ──today──► [img A]│ snip@sha256:9c1f… ──► [img A]│ always │ snip:1.4.2 │ ─tomorrow► [img B]│ snip@sha256:9c1f… ──► [img A]│ always └──────────────┘ └────────────────────────────┘ friendly but can move ugly but cannot lieThe rule that falls out: tags for humans, digests for machines. Tag images for readability, but when you deploy, pin to a digest so the thing that runs is provably the thing you tested.
Why latest is dangerous
Section titled “Why latest is dangerous”latest is the tag a tool assumes when you specify none. It looks like it means “the newest version.”
It does not. latest is just a tag like any other — a mutable label that happens to be the default.
It points at whatever was last pushed without a specific tag, which may not be the newest, the stable
one, or the one you tested.
Now play it forward. You deploy myapp:latest. It works. A week later a node restarts and re-pulls
myapp:latest — but latest was moved in the meantime, so the node now runs different code than its
neighbours. Nothing in your config changed; your fleet silently went inconsistent.
This is the recurring thread in negative: latest adds a manual, error-prone uncertainty —
“which image is actually running right now?” — and a pinned digest removes it. Immutable references
make production safer because they make “what’s deployed” a fact, not a guess.
Public vs private registries
Section titled “Public vs private registries”| Public | Private | |
|---|---|---|
| Who can pull | Anyone | Authenticated users only |
| Typical use | Base images, open-source software | Your proprietary application images |
| Examples | Docker Hub public repos, GHCR public | ECR, GHCR private, Harbor, self-hosted |
| Auth to pull | None | docker login / cloud credentials |
You’ll use both: pull base images (python, node, nginx) from public registries, and push your
images to a private one so your source-bearing artifacts aren’t world-readable.
Image provenance: where did this come from?
Section titled “Image provenance: where did this come from?”The deepest registry question is trust: can you prove this image is the one your pipeline built, from the source you reviewed, and that no one tampered with it in between? That’s provenance.
The building blocks: a digest proves integrity (these are the exact bytes). A signature (Sigstore/cosign) proves origin (your CI’s key signed it). An SBOM (Software Bill of Materials) lists what’s inside so you can answer “are we exposed to this CVE?” without guessing. Together they let you require, at deploy time, that you only run images signed by your own pipeline. This is the foundation of supply-chain security — and it’s only possible because images are content-addressed in the first place. A name can be forged; a digest can’t.
The thread
Section titled “The thread”A registry turns “the image exists on my laptop” into “the exact same image is available to every machine that needs it.” But sharing is only safe if references are honest. Tags are convenient and mutable; digests are immutable and verifiable. The manual, error-prone step a digest removes is the question “is the thing running in production really the thing I built and tested?” — replacing a hopeful yes with a checkable one. That is how registries, used with digests and provenance, make production safer: they make the identity of what you ship a fact you can prove, not a label you have to trust.
The architect’s lens
Section titled “The architect’s lens”A registry is the distribution layer you choose to put between build and run — answer the five before you trust it:
- Why does it exist? Because “build once, run everywhere” is meaningless if the image only lives on your laptop. The registry is the shared store — Docker Hub, GHCR, ECR, Harbor — that gets the same image to CI, your teammate, and every production node, transferring only the content-addressed layers a machine doesn’t already have.
- What problem does it solve? Honest, verifiable identity for what you ship. A digest
(
@sha256:…) is an immutable fingerprint — pull it and you get those exact bytes or an error — turning “is the thing in prod really the thing I tested?” from a hopeful yes into a provable one, and is the foundation provenance (signature + SBOM) builds on. - What are the trade-offs? Tags are mutable and convenient; lean on them and you get silent drift —
latestis the worst case, since the same deploy command runs different software depending on when it runs, breaking reproduction and rollback. Public base images are someone else’s code, and anonymous Docker Hub pulls are rate-limited (~100 pulls / 6h per IP), which can fail a busy CI fleet behind one NAT with no code change. - When should I avoid it / what to avoid? Never deploy by
latest(or any bare tag) — pin a real version and deploy by digest. Don’t push source-bearing images to a public registry; push those to a private one and pull only base images from public sources, pinned by digest and scanned. - What breaks if I remove it? Without a registry, images can’t reach the machines that need them and “run everywhere” collapses. Without digests/provenance you lose integrity — what’s deployed becomes a label you trust rather than a fact you can prove, and a forged name can put code you never built into production (supply-chain security).
Check your understanding
Section titled “Check your understanding”- What does a registry do, and why does pushing/pulling only transfer some layers?
- Explain the difference between a tag and a digest, and the rule “tags for humans, digests for machines.”
- Why is
latestnot “the newest version,” and walk through how it can make two identical-looking nodes run different code. - When would you pull from a public registry versus push to a private one? Name one risk of public base images and one mitigation.
- Define provenance and name the three building blocks (digest, signature, SBOM) and what each proves.
Show answers
- A registry is the shared store images are pushed to and pulled from, distributing “build once, run everywhere” to every machine. Only some layers transfer because images are content-addressed layers: the registry already has shared base layers, so push/pull moves just the diff.
- A tag (
snip:1.4.2) is a mutable, human-friendly pointer that can be moved to different content; a digest (snip@sha256:…) is the immutable hash of the exact bytes. “Tags for humans, digests for machines” — tag for readability, but deploy by digest so what runs is provably what you tested. latestis just the default tag, a mutable label pointing at whatever was last pushed without a specific tag — not necessarily the newest or stable build. If a node restarts and re-pullsmyapp:latestafter it was moved, that node runs different code than its neighbours even though no config changed — silent fleet inconsistency.- Pull base images (
python,node,nginx) from a public registry; push your source-bearing images to a private one so they aren’t world-readable. A public base image is someone else’s code (risk); mitigate by pinning it by digest so it can’t be swapped out and by scanning it. - Provenance is being able to prove an image is the one your pipeline built from reviewed source, untampered. A digest proves integrity (exact bytes); a signature (Sigstore/cosign) proves origin (your CI’s key signed it); an SBOM lists what’s inside so you can answer CVE-exposure questions.