Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Container registries: understanding image storage and distribution

By updated

Improve this page

You have built a container image on your workstation. How do you deploy it to your production servers, and how do you make sure the whole team uses exactly the same version? The answer is a registry: a server that stores and distributes your images, like GitHub for code.

This lesson explains how a registry works under the hood, from layered storage to the pull and push protocol, then how to secure it: authentication, immutable references by digest, separated rights and image scanning. It is for anyone who publishes or consumes images, and prepares the lessons on signing and the supply chain.

What you will learn

  • Explain what a registry is and its vocabulary (repository, tag, digest)
  • Describe how an image is stored: the manifest and layers model
  • Follow what docker pull and docker push do under the hood
  • Distinguish a mutable tag from an immutable digest, and choose the right one in production
  • Choose a type of registry: public, cloud, private
  • Apply the five security rules of a production registry

What is a container image registry?

A registry is the mandatory waypoint for any image between its build and its execution. Understanding what it stores, and under which name, is the basis of everything that follows, in particular the distinction between tag and digest.

Simple definition

A registry is a specialised storage server that stores container images, distributes them to whoever needs them and versions them with labels. It is, for images, what a Git forge is for source code.

Key vocabulary

Four terms come up constantly, and mixing them up leads to deployment errors. The table distinguishes them, with an example for each; the digest is the one that matters most for security.

TermDescriptionExample
RegistryServer that hosts imagesDocker Hub, Harbor, AWS ECR
RepositorySpace grouping all versions of the same imagenginx, my-app, postgres
TagReadable label that designates a version1.29.0, stable, latest
DigestUnique and immutable SHA256 fingerprint of the contentsha256: followed by 64 hexadecimal characters

Image vs container: the difference

An image is a static package, stored in the registry: it is the mould. A container is a running application, created from that image: it is the moulded object that runs. The registry only holds images, never containers.

Why use a registry?

Without a registry, you have to deploy by hand: copy the code, install dependencies, manage versions on every server. It quickly becomes unmanageable beyond a few servers, and every manual copy is an opportunity for drift between environments.

The 4 concrete benefits

A registry brings four benefits that become decisive as the team and the number of deployments grow. The last one, CI/CD integration, is what makes the other three automatic.

  1. Reproducibility: the same image everywhere, in development, staging and production. No more "works on my machine".
  2. Fast deployments: one build, N servers, and downloads sped up by layer deduplication.
  3. Traceability: who published which version, when, under which tag.
  4. CI/CD integration: the pipeline builds the image, pushes it to the registry, and the orchestrator (Kubernetes, Nomad) pulls it automatically.

Why does it work with Docker, Podman and Kubernetes?

You may use Docker, Podman, containerd or Kubernetes. These tools all talk to the same registry because they follow open standards: you can switch tools or registries without rewriting your scripts, and that avoids lock-in to a vendor.

The OCI standards (Open Container Initiative)

The container ecosystem relies on the specifications of the OCI (Open Container Initiative), which defines a common image format and distribution API, adopted by the whole ecosystem. Docker, Podman and containerd therefore talk to registries through the same HTTP endpoints /v2/..., and an image built with Docker runs in Kubernetes, which uses containerd under the hood.

The OCI publishes three specifications: the Image Spec (image format: manifests, layers, configuration), the Runtime Spec (how to run a container) and the Distribution Spec (the HTTP API to push and pull images). The Open Container Initiative website details them.

How is an image stored?

An image is not one big file: it is a layered system organised by a JSON manifest. This split explains both the speed of downloads and the integrity check.

The "manifest + layers" model

An image breaks down into three elements. The manifest is a JSON document that lists the image's components and their stacking order. The layers carry the filesystem content, each RUN or COPY instruction of a Dockerfile producing one. The config blob gathers the runtime metadata, including CMD, ENV and the default user.

Structure of an image: a JSON manifest points to a config blob (CMD, ENV, user) and to layers each identified by its SHA256.JSON manifestordered list of componentsConfig blobCMD, ENV, userLayer 1sha256:...Layer 2sha256:...Layer 3sha256:...
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

Each layer is identified by its SHA256 digest. If two images share the same layer, for example the same base image, it is stored and downloaded only once: this is deduplication. It saves network and storage, and the SHA256 guarantees that a layer cannot be modified without its fingerprint changing.

How does docker pull work?

When you type docker pull nginx:1.29.0, the client follows five steps. Understanding them helps diagnose a slow download or an integrity error, and shows where layer verification happens.

The five steps of a docker pull: request the manifest, receive the list of layers, download only the missing layers, check each SHA256, assemble the local image.1. Request the manifest GET /v2/.../manifests/tag2. Receive the list of layers3. Download missing layers4. Check each SHA2565. Assemble the local image
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %
  1. Manifest request: the client sends GET /v2/library/nginx/manifests/1.29.0.
  2. Manifest received: the registry answers with the list of layers and their SHA256.
  3. Missing layers download: the client only downloads the ones it does not already have locally.
  4. Verification: each layer is checked against its fingerprint; a single modified bit makes the download fail.
  5. Assembly: the client stacks the layers in order, and the image is ready to run.

How does docker push work?

The reverse process publishes a locally built image. It relies on the same deduplication: only the layers missing from the registry travel, which keeps successive publications of the same application light on the network.

The four steps of a docker push: compute each layer's SHA256, query the registry with HEAD, upload only the missing layers, publish the manifest that binds the tag.1. Compute each layer's SHA2562. HEAD on the registry does this layer exist?3. Upload only missing layers4. Publish the manifest PUT /v2/.../manifests/tag
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

The client computes the SHA256 of each layer, queries the registry with a HEAD request to know whether it already exists, uploads only the missing layers, then publishes the manifest with PUT /v2/.../manifests/<tag>. This last step binds the tag to the content: a tag is just a pointer to a manifest.

How do you secure access with authentication?

A registry can be public (anyone can pull images) or private (authentication required). In every case, publishing must be authenticated: whoever can push an image can get their code executed wherever it is deployed.

The Bearer Token scheme (Docker Registry v2 standard)

On a private registry, a request without authentication gets a 401 Unauthorized response with a WWW-Authenticate: Bearer realm="..." header pointing to the token service. The client authenticates there with its credentials, receives a bearer token, then replays its request with the Authorization: Bearer <token> header.

This token is deliberately short-lived: the Distribution specification sets its validity to 60 seconds by default. The scheme separates authentication (checking identity, including through LDAP, OAuth or OIDC) from authorisation (checking the right to pull or push on a given repository).

How do you read an image name?

The full name of an image follows a precise format, several parts of which are implicit. Knowing these defaults avoids pulling an image from an unexpected registry, a classic substitution vector:

[registry/][namespace/]repository[:tag|@digest]

Concrete examples

The table breaks down common names. Note that nginx alone actually means docker.io/library/nginx:latest: a short name depends on the client configuration and on a mutable tag.

NameRegistryNamespaceRepositoryTag or digest
nginxdocker.io (implicit)library (implicit)nginxlatest (implicit)
nginx:1.29.0docker.iolibrarynginx1.29.0
myorg/api:2.0.1docker.iomyorgapi2.0.1
harbor.example.com/prod/api:stableharbor.example.comprodapistable
nginx@sha256:<digest>docker.iolibrarynginxSHA256 digest

Tags vs digests: the critical distinction

An image is referenced by a tag or by a digest, and this choice decides a deployment's reproducibility. It is the most important distinction of this lesson for supply chain security.

Tags (mutable)

A tag is mutable: latest, stable or even 1.2 can point to different images over time, and anyone allowed to push can move it. Handy to read and share, it does not guarantee that docker pull nginx:latest today gives the same image as tomorrow.

Digests (immutable)

A digest is immutable: sha256:... always designates the same image, bit for bit. It guarantees integrity and makes a deployment reproducible; its only drawback is being hard to read, with 64 hexadecimal characters. The image:tag@sha256:... form combines both: the tag for humans, the digest for machines.

Best practices

In production, the rule is simple: never latest, always at least a versioned tag, and preferably a digest. A redeployment that pulls a latest moved in the meantime can break the application, or install a compromised image.

Multi-architecture: one image, several platforms

Some images, such as nginx or alpine, exist for several platforms: linux/amd64, linux/arm64, sometimes windows/amd64. The same image name then serves machines of different architectures without the user having to pick a variant.

How does it work?

The registry stores a manifest index (or manifest list) that points to one manifest per platform. The client detects its system and architecture, then pulls the right manifest: a developer on an ARM64 Mac and an x86_64 server thus use the same reference. Beware: the digest of the index and that of a platform differ, which matters when you pin.

Multi-architecture image: a manifest index points to one manifest per platform (linux/amd64, linux/arm64, windows/amd64), and the client pulls the one for its platform.Manifest indexone image, several platformsManifestlinux/amd64Manifestlinux/arm64Manifestwindows/amd64The client pullsits platform's manifest
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

Which types of registries exist?

There are three families of registries: public, managed in the cloud and private self-hosted. The choice depends mostly on data sovereignty, integration with your cloud and the acceptable operational load.

Public registries

For open source, prototypes and sharing. Docker Hub's download limits are worth knowing: in CI, they are the first cause of unexpected failures, hence the value of a local cache.

RegistryCharacteristicsLimits
Docker HubHistorical public registry, free for public images100 pulls per 6 h unauthenticated, 200 with a Personal account
GitHub Container Registry (ghcr.io)Integrated with GitHub ActionsStorage quotas depending on the plan
Quay.io (Red Hat)Vulnerability scanning, automatic buildsPaid for private use

Managed cloud registries

For companies already on a cloud: integration with the provider's IAM avoids managing separate credentials, and operations are delegated. Prices are indicative and vary by region and usage.

RegistryStrengthsIndicative price
AWS ECRIAM integration, vulnerability scanningAbout $0.10 per GB per month
Google Artifact RegistryMulti-format (images, Maven, npm), scanningAbout $0.10 per GB per month
Azure Container RegistryGeo-replication, AKS integrationFrom about $5 per month (Basic)

Private on-premise registries

For data sovereignty, compliance or disconnected environments. The counterpart is operations: high availability, backups and updates remain your responsibility.

RegistryStrengthsComplexity
Distribution (formerly Docker Registry)Reference open source, simpleBasic, no graphical interface
HarborInterface, RBAC, built-in Trivy scanning, replicationKubernetes deployment recommended
JFrog ArtifactoryMulti-format, high availability, LDAP/SAMLPaid, complex setup
Sonatype NexusMulti-format, Maven-orientedJava ecosystem

A few selection guidelines: Docker Hub for a prototype, ghcr.io for CI/CD on GitHub, ECR on AWS, Harbor when compliance requires full control. The Docker Hub, Quay.io and Distribution guides (in French) detail each one.

What are the best practices for production?

Five rules cover the essentials of a production registry's security. They reinforce each other: an analysed image referenced by a mutable tag can be replaced after the analysis.

1. Never store secrets in images

A secret written in a layer stays readable with docker history or by extracting the layers, even if it is deleted in a later layer. The secret is injected at runtime, from a secrets manager, never at build time:

# ❌ BAD: the secret stays readable in a layer of the image
RUN echo "API_KEY=abc123" >> /app/.env
# Kubernetes: the value comes from a Secret, never from the image
env:
- name: API_KEY
valueFrom:
secretKeyRef:
name: api-credentials
key: api-key

2. Version images strictly

Strict versioning enables traceability, quick rollback and reproducibility. You publish semantic tags (1.2.3) or tags dated with the commit, never latest in production, and the deployment manifest references the digest:

spec:
containers:
- name: api
image: harbor.example.com/prod/api:1.4.2@sha256:<digest>

3. Use access tokens, not passwords

Access tokens can be revoked one by one, renewed easily and carry limited rights: a read-only token for deployments, a write token for CI only. Pass them through standard input, never as an argument visible in the shell history or the process list:

Terminal window
docker login registry.example.org -u robot-ci --password-stdin < "$TOKEN_FILE"

Better still, in CI: a workload identity through OIDC replaces the long-lived token, as shown in the lesson OIDC: authenticating without a secret.

4. Separate pull and push rights

Least privilege applies to the registry: most consumers only need to pull. CI/CD pipelines push, production servers only pull, and developers pull from prod/* but push to dev/*. With Harbor, this translates into per-project roles:

Project "prod": developers = Guest (read only)
Project "dev": developers = Developer (read and write)

5. Scan images before deployment

You do not expose to production an image carrying critical known vulnerabilities. Trivy has been the scanner built into Harbor by default since version 2.2; Clair remains Quay's; Grype is another open source option. The recommended flow scans in staging, then promotes:

build the image
push to the staging registry
scan with Trivy or Grype
if a fixable critical vulnerability: fail the pipeline
otherwise: promote to the production registry, by digest

Troubleshooting: frequent problems

Registry errors look alike, but their cause shows in the HTTP code or the message. The table pairs the most frequent ones with their likely cause and their fix, without resorting to dangerous workarounds.

SymptomLikely causeSolution
401 UnauthorizedExpired or missing tokenLog in again with docker login
429 Too Many RequestsDocker Hub download limitAuthenticate, or go through a local cache
manifest unknownMissing tag or typoList tags with crane ls
Slow downloadDistant registry, no cacheUse a local cache (Harbor proxy)
x509 certificate errorUnknown certificate authorityAdd the authority to the trust store, rather than disabling TLS

Key points

  1. A registry stores and distributes images centrally, like a Git forge for code.
  2. An image is a manifest and layers identified by their SHA256, deduplicated and checked on every download.
  3. The OCI standards make Docker, Podman and Kubernetes speak the same /v2/ protocol.
  4. A tag is mutable, a digest is immutable: in production, reference by digest and refresh it regularly.
  5. A registry token is short-lived and separate from authorisation, which distinguishes pulling from pushing.
  6. Five rules in production: no secret in images, strict versioning, tokens rather than passwords, separated rights, scanning before promotion.

Knowledge check

Check that you have the essentials of this guide. The questions only cover what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn