Image Distribution

Image Distribution

Bitnesia Sep 12, 2026 10 ID

After an image is successfully pushed to a registry, whether to Docker Hub or a self-hosted private registry, the work of a Sysadmin/DevOps Engineer is far from complete. The image still needs to flow from the development environment to staging and then to production in a traceable manner, without the risk of confusing tags or images suddenly having different contents despite sharing the same tag name. On the other hand, teams operating multiple servers or clusters often require a way to pull images faster without repeatedly straining the source registry, while simultaneously ensuring that circulating images do not harbor vulnerabilities that could compromise production systems. This chapter covers consistent tagging strategies, image promotion patterns across environments, mirror registries for efficient distribution, and vulnerability scanning as integral parts of a secure image distribution workflow.

21.1 Tagging Strategies

Tags are the sole identifier distinguishing one image version from another within a single repository. A poor tagging strategy makes it difficult for teams to track which version is currently running in production or, worse, leads deployments to inadvertently pull the wrong image. This section explores common tagging patterns used in practice alongside pitfalls to avoid.

21.1.1 The latest Tag Problem

The latest tag is not an automated marker for the newest version despite its name. It is merely a standard tag name assigned by Docker by default when no specific tag is specified during docker build or docker tag. Because its content can change at any time whenever a new push occurs with the same tag, relying on latest in production means there is no guarantee that an image pulled today will match the one pulled last week.

docker pull nginx:latest
docker pull nginx:latest

The two commands above appear identical, yet they can result in images with different digests if the nginx maintainer pushes a new version with the latest tag between those pulls. In real-world environments, this behavior frequently causes "works on my machine, fails on the server" bugs, which are actually caused by unnoticed image version mismatches. The latest tag remains acceptable for local development or documentation examples, but should be avoided in Dockerfile references, docker-compose.yml files, or deployment manifests targeted for staging and production environments.

21.1.2 Semantic Versioning

The most common tagging pattern for application images is semantic versioning (semver), structured as MAJOR.MINOR.PATCH, which is also utilized by official base images such as node or postgres. The MAJOR number increments for backward-incompatible changes, MINOR increments for backward-compatible new features, and PATCH increments for bug fixes.

docker build -t registry.internal.perusahaan.com/payment-service:1.4.2 .
docker push registry.internal.perusahaan.com/payment-service:1.4.2

In addition to complete version tags, common practice involves pushing multiple tags simultaneously for the exact same image, such as 1.4.2, 1.4, and 1. This allows consumers to choose their desired level of specificity: locking to a specific patch version for maximum stability, or following the latest minor version to automatically receive bug fixes.

docker tag payment-service:1.4.2 registry.internal.perusahaan.com/payment-service:1.4
docker tag payment-service:1.4.2 registry.internal.perusahaan.com/payment-service:1
docker push registry.internal.perusahaan.com/payment-service:1.4
docker push registry.internal.perusahaan.com/payment-service:1

Verify whether all three tags truly reference the exact same image by comparing their digests via docker images --digests. Identical values in the DIGEST column across rows for 1.4.2, 1.4, and 1 confirm that these tags are simply different labels pointing to one single image rather than three separate images.

docker images --digests registry.internal.perusahaan.com/payment-service

21.1.3 Git-Based Tagging

For CI/CD pipelines building images on every commit, semver-based tagging alone is often insufficiently granular because not every commit results in a new version release. A widespread pattern used by Developers and Sysadmins/DevOps Engineers is tagging images with the git commit SHA (typically the short version), ensuring every build receives a unique tag traceable directly back to the exact commit that generated it.

GIT_SHA=$(git rev-parse --short HEAD)
docker build -t registry.internal.perusahaan.com/payment-service:$GIT_SHA .
docker push registry.internal.perusahaan.com/payment-service:$GIT_SHA

SHA-based tags are immutable by convention: a single SHA will never be reused for different image contents. This makes it safe to reference in deployment manifests without risking silent content changes like those associated with the latest tag. A common industry combination pairs SHA tags for precise tracing and rollback capabilities with semver tags for official releases communicated to other teams.

21.2 Image Promotion Pipeline

Once a tagging strategy is established, the next question is how the exact same image moves from development to staging and then to production. This section discusses the image promotion pattern, which involves transferring verified images between environments without rebuilding them from source code.

21.2.1 Build Once, Deploy Many

The core principle of image promotion is build once, deploy many: an image is built a single time by the CI pipeline, and that exact same image, with an identical digest, is promoted sequentially to each environment after passing tests in the preceding stage. This contrasts with an erroneous yet common pattern of rebuilding separate images for each environment under the assumption that the output will be identical. Rebuilding introduces subtle discrepancies, such as unpinned dependency versions changing between build times, meaning an image passing staging tests might not be identical to what runs in production.

In practice, promotion means re-tagging an existing image with a new tag indicating its target environment, without executing docker build again.

docker pull registry.internal.perusahaan.com/payment-service:a1b2c3d
docker tag registry.internal.perusahaan.com/payment-service:a1b2c3d \
  registry.internal.perusahaan.com/payment-service:staging
docker push registry.internal.perusahaan.com/payment-service:staging

Once the image carrying the staging tag passes testing, the production tag is attached to the exact same digest rather than a newly built binary output.

docker tag registry.internal.perusahaan.com/payment-service:a1b2c3d \
  registry.internal.perusahaan.com/payment-service:production
docker push registry.internal.perusahaan.com/payment-service:production

21.2.2 Digest Verification, Not Just Tags

Because tags are essentially mutable pointers, verifying image promotion solely by checking tag names provides insufficient guarantee. A more reliable approach is comparing the digest, the SHA-256 hash value of the image manifest that changes whenever the contents change, ensuring that the image executing in production matches the tested artifact in staging completely.

docker inspect --format='{{index .RepoDigests 0}}' payment-service:staging
docker inspect --format='{{index .RepoDigests 0}}' payment-service:production

Many orchestration platforms, such as Kubernetes, support referencing images directly via digest (image@sha256:...) instead of relying solely on name and tag combinations. This allows production deployments to lock to a specific, verified digest rather than depending on tags whose underlying image contents could technically be overwritten in the registry.

21.2.3 Automated Promotion in CI/CD Pipelines

In production environments, manual execution of re-tagging and pushing steps for promotion is rare. Instead, this functions as an automated stage within a CI/CD pipeline following successful test stages. The typical workflow: a build stage creates an image tagged with the commit SHA, a test stage runs checks against that image, and a deploy stage promotes that exact image to the target environment only if all prior stages succeed. Production promotion within this pattern ideally incorporates an additional approval gate, such as manual sign-off by a responsible Sysadmin/DevOps Engineer, or automated criteria like vulnerability scan results free of high-risk findings.

21.3 Mirror Registry

As more servers or cluster nodes pull images from a single registry, network load increases and the risk of hitting pull rate limits rises, especially with public images from Docker Hub that restrict anonymous and free-tier pulls within specific time windows. A mirror registry mitigates this by maintaining local copies of images closer to the consuming nodes.

21.3.1 Registry Mirror as a Pull-Through Cache

Distribution supports a pull-through cache mode, acting as a mirror for an upstream registry (typically Docker Hub): when an image is requested for the first time, the mirror fetches it from the source registry and caches a copy locally. Subsequent requests for the same image are served directly from cache without reaching out to the source registry. This mode is configured via the proxy block in config.yml.

version: 0.1

storage:
  filesystem:
    rootdirectory: /var/lib/registry

http:
  addr: :5000

proxy:
  remoteurl: https://registry-1.docker.io
docker run -d -p 5000:5000 --restart=always --name registry-mirror \
  -v /mnt/mirror-data:/var/lib/registry \
  -v "$(pwd)"/config.yml:/etc/distribution/config.yml \
  registry:3

A registry configured with proxy.remoteurl operates as read-only for pull operations and does not accept direct image pushes; its sole purpose is functioning as a caching layer in front of the upstream registry.

21.3.2 Directing Docker Engine to the Mirror

To automatically route all docker pull requests on a host through the mirror without altering individual image names, configure the Docker Engine via the registry-mirrors option inside daemon.json.

{
  "registry-mirrors": ["https://registry-mirror.internal.perusahaan.com"]
}
sudo systemctl restart docker

With this configuration active, commands like docker pull nginx:1.27 continue using Docker Hub references as usual, but the Docker Engine automatically attempts pulling through the mirror first before falling back to the upstream registry if the mirror is unreachable. Sysadmins/DevOps Engineers operating clusters with dozens or hundreds of nodes rely on this pattern because nodes only need to fetch popular images once via the mirror, giving all other nodes immediate cache access without burdening Docker Hub or exhausting rate limits.

21.3.3 Verifying Mirror Operations

Ensure that the Docker Engine properly loads the mirror configuration by running docker info, which displays active registry mirrors under the Registry Mirrors section.

docker info --format '{{.RegistryConfig.Mirrors}}'

If the mirror is enabled but pulls remain slow, review the mirror registry container logs to ensure proxy requests to the upstream registry execute without errors, and check network connectivity between the node and the mirror. An unreachable mirror will not cause pulls to fail completely as long as Docker Engine can fall back to the primary registry, but cache benefits are lost for that pull session.

21.4 Scanning and Vulnerabilities

An image successfully exiting a build pipeline is not automatically secure. Base images can carry system packages containing publicly known vulnerabilities, and application dependencies installed inside them may contain security flaws discovered later. This section covers vulnerability scanning as a mandatory step prior to distributing images into staging or production.

21.4.1 Why Scanning Must Be Integrated into Pipelines

Attackers discovering containers with unpatched vulnerabilities can leverage them as initial access points to execute arbitrary code within the container, exfiltrate sensitive data, or perform container escapes to the underlying host. Because new vulnerabilities are continuously identified in existing software packages, scanning is not a one-time task during initial image creation. It must be a recurring, automated process executing on every new image build and periodically against existing images stored in the registry.

21.4.2 Scanning with Docker Scout

Docker Scout is an integrated vulnerability scanning tool built directly into the Docker CLI and Docker Hub, utilizing the Common Vulnerabilities and Exposures (CVE) database to match packages found across image layers. Execute a quick scan using the docker scout quickview subcommand to display a summary of identified vulnerabilities grouped by severity level.

docker scout quickview payment-service:1.4.2

Docker Scout comes pre-installed with Docker Desktop. On Linux servers running only Docker Engine without Docker Desktop, the CLI plugin may need to be installed manually. If executing docker scout returns docker: 'scout' is not a docker command, the plugin is missing from the installation. Install it via the official docker-scout-plugin package provided by Docker's package repositories for your distribution, then rerun the command.

To inspect detailed CVE lists alongside affected packages and remediated versions, use the cves subcommand.

docker scout cves payment-service:1.4.2

Docker Scout categorizes findings by severity according to the Common Vulnerability Scoring System (CVSS) standards: critical, high, medium, and low. Sysadmins/DevOps Engineers typically enforce policies blocking images with critical findings from promotion to production until remediated, while low or medium issues can be scheduled for patching without blocking release workflows.

21.4.3 Scanning with Trivy

Trivy is an open-source vulnerability scanner developed by Aqua Security. It is widely used due to its independence from Docker Hub and its ability to scan container images, file systems, and Git repositories using a single tool. Trivy is available as an official container image, enabling direct image scans without installing dedicated host binaries.

docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image payment-service:1.4.2

The command above mounts docker.sock into the Trivy container, allowing it to inspect images stored inside the host Docker Engine instance. To fail CI/CD builds when specific vulnerability thresholds are detected, Trivy provides the --exit-code and --severity flags to exit with a non-zero code upon detecting matches.

docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image --exit-code 1 --severity CRITICAL,HIGH payment-service:1.4.2

This approach allows CI/CD pipelines to abort automatically whenever Trivy identifies CRITICAL or HIGH vulnerabilities, preventing high-risk images from proceeding to push or promotion steps.

21.4.4 Reducing the Attack Surface

Vulnerability scan outputs remain significantly cleaner when an image's attack surface is minimized during the initial build phase rather than addressed reactively after findings emerge. Effective field practices include: selecting minimal base images such as alpine variants or distroless images that bundle far fewer system packages compared to full base images; performing periodic image rebuilds even when application source code remains unchanged to ensure underlying system packages update to patched versions; and stripping build tools and development dependencies from final images using multi-stage builds. Combining these techniques does not replace routine scanning, but it substantially reduces the volume of findings requiring remediation during each scan cycle.