Skip to content

Docker Registry — Real World Distribution


Every image you’ve pulled so far has come from somewhere — you just haven’t had to think about where. Behind alpine, behind every docker image pull you’ve run, there’s a server that stores layers, tracks tags, and hands them out on request. That server is a registry, and tarballs stop being a sensible distribution mechanism the moment more than one person or machine needs the same image.

Why Tarballs Don’t Scale

docker image save and docker image load work fine for moving one image between two machines you control. But think about what breaks down once you’re not the only one involved:

  • No central source of truth. If ten teammates need the same image, someone has to save it, upload it somewhere, and everyone else has to load it manually — and now there are ten copies with no way to know if they’re actually identical.
  • No versioning. A tarball is just a file. If the image changes, you either overwrite the old tarball (and lose the previous version) or invent your own naming scheme to track versions yourself.
  • No incremental transfer. Every save ships every layer, every time, even if the receiving machine already has 90% of them from a previous pull.

A registry solves all three: one address everyone pulls from, tags that behave like version labels, and layers that transfer only when the receiving side doesn’t already have them.

What a Registry Actually Is

A registry is a server that stores images as two things:

  1. Layers — the actual filesystem content, stored once and addressed by the hash of their contents. If two images share a layer (say, the same base OS), the registry only needs to store it once.
  2. Manifests — a small JSON document per image tag that lists which layers make it up, in which order, plus metadata like the default command and exposed ports.

When you pull an image, Docker doesn’t download one big file — it downloads the manifest first, then fetches only the layers listed in it that you don’t already have locally.

    sequenceDiagram
    participant D as Your Docker Engine
    participant R as Registry

    D->>R: Request manifest for "alpine:3.19"
    R-->>D: Manifest (list of layer digests)
    D->>D: Check which layers already exist locally
    D->>R: Request only the missing layers
    R-->>D: Layer data
    D->>D: Assemble image from local + new layers
  

This is why pulling a new tag of an image you already have is often nearly instant — most of the layers are already sitting on your machine.

Tags vs Digests

A tag (the 3.19 in alpine:3.19) is a human-friendly label that a registry maps to a specific manifest — but that mapping can change. If someone pushes a new image and reuses the tag 3.19, the tag now points somewhere else. Nothing about the tag itself guarantees you’ll get the same bytes twice.

A digest is the opposite: a hash of the manifest’s exact content, written as sha256:.... Digests are immutable by definition — the moment the content changes, the hash changes, so a digest always refers to one exact image, forever.

Tip

You can pull by digest instead of tag when you need a guarantee that you’re getting an exact, unchanging image: docker image pull alpine@sha256:<digest>. This matters more once you’re pulling images you don’t control yourself.

What’s Next

You now know what a registry does structurally — manifests, layers, tags, digests. But “a registry” has been an abstract concept so far, and in practice, one registry has become so dominant that people often use its name and the generic term interchangeably. Next, we’ll pull that apart: what Docker Hub specifically is, and where it fits (or doesn’t) in the wider registry landscape.

Last updated on