✦ For everyone, free.

Practical knowledge for real and everyday life

Home

17.1.2.3 Production Digest Preference

A focused guide to Production Digest Preference, connecting core concepts with practical Docker and container operations.

Production digest preference is the practice of deploying an application's own built image by its specific content digest, rather than by a tag, distinct from base image pinning during the build, since this concerns the deployment-time reference used to actually run a specific, already-built artifact in production, where certainty about exactly what is running matters as much as, or more than, certainty about what went into building it.

Why deployment-time references need the same rigor as build-time ones

A tag like my-api:1.4.2, even one that appears to represent a specific, fixed version, can be accidentally or maliciously retagged to point at different content after the fact, since tags are inherently mutable references that any sufficiently privileged push can redirect:

docker pull my-api:1.4.2
docker push registry.example.com/my-api:1.4.2

A second push to the identical tag, whether through an accidental rebuild reusing the same version number or a deliberate, unauthorized action, silently changes what that tag actually points to, which means a deployment system relying on the tag rather than a digest could pull genuinely different content on a later deployment than what was originally tested and approved under that same-looking reference.

Capturing the digest at build time for downstream use

A deployment pipeline should capture the exact digest produced by a build immediately, then pass that specific digest through every subsequent stage, rather than re-resolving a tag at each step and risking it having changed in the interim:

DIGEST=$(docker build -q -t my-api:1.4.2 . | docker inspect --format='{{index .RepoDigests 0}}')
echo "$DIGEST" > image-digest.txt
docker push registry.example.com/my-api:1.4.2
DIGEST=$(docker inspect registry.example.com/my-api:1.4.2 --format='{{index .RepoDigests 0}}')

Carrying this captured digest forward through staging, approval, and production deployment stages, rather than relying on the tag at each step, ensures every stage is genuinely working with the exact same content the build originally produced, with no possibility of drift introduced by a tag being moved in between.

Deploying by digest in orchestration configuration

Whatever deployment mechanism is in use, Compose, a Kubernetes manifest, or another orchestrator's configuration, referencing the image by digest rather than tag in the actual deployment specification is what closes the loop and guarantees the running container matches exactly what was tested:

services:
  api:
    image: registry.example.com/my-api@sha256:3f29a8c1d8e2b4f6a9c7d5e8b1f3a6c9d2e5f8b1a4c7d0e3f6a9c2d5e8b1f4a7
spec:
  containers:
    - name: api
      image: registry.example.com/my-api@sha256:3f29a8c1d8e2b4f6a9c7d5e8b1f3a6c9d2e5f8b1a4c7d0e3f6a9c2d5e8b1f4a7

This is the actual point where digest preference provides its concrete benefit: the deployment specification itself unambiguously identifies exactly which content is running, verifiable directly against the digest, rather than trusting that a tag still points to what it pointed to when it was last verified.

Precision during rollback

When rolling back to a previous version, referencing the exact digest of that previous deployment, rather than a tag that might have since been reused or moved, guarantees the rollback target is genuinely the same content that was previously running and verified, not a coincidentally same-numbered but potentially different rebuild:

kubectl set image deployment/my-api api=registry.example.com/my-api@sha256:8a1f3c9b2e7d...

Maintaining a deployment history that records the actual digest deployed at each point in time, rather than only the tag, makes this kind of precise, confident rollback possible without needing to reconstruct or guess at exactly what digest a given historical tag corresponded to at the time it was deployed.

GitOps patterns and digest tracking

In a GitOps deployment model, where the desired state is declared in version-controlled configuration and a separate process reconciles the running environment to match it, recording the deployed image by digest directly within that version-controlled configuration provides an auditable, precise history of exactly what was deployed and when, fully readable from the commit history itself:

git log -p -- deployment.yaml | grep "image:"

This makes "what was running in production on a specific date" a question answerable directly from version control, rather than requiring a separate system of record to track digest-to-deployment-time mapping independently.

The convenience trade-off and how to manage it

Working exclusively with digests is less convenient for humans than working with readable tags, and the practical resolution most teams adopt is maintaining both: a human-readable tag for general reference and communication, paired with the actual digest as the authoritative reference used by automated deployment tooling itself:

docker tag registry.example.com/my-api@sha256:3f29a8c1... registry.example.com/my-api:1.4.2

This preserves the readability and convenience tags provide for human communication, release notes, and casual reference, while ensuring the actual, automated deployment mechanism relies on the unambiguous digest rather than the inherently mutable tag.

Common mistakes

  • Relying on tags throughout a deployment pipeline, allowing a tag's underlying content to potentially change between when it was tested and when it is actually deployed.
  • Not capturing and propagating the build's actual digest through every subsequent pipeline stage, instead re-resolving the tag at each step and risking drift.
  • Rolling back to a previous tag without confirming it still corresponds to exactly the same digest that was actually verified and running previously.
  • Recording only the deployed tag in deployment history or GitOps configuration, losing the precise, auditable record that the actual digest would have provided.
  • Abandoning human-readable tags entirely in favor of digests everywhere, sacrificing convenience and communication clarity that a paired tag-and-digest approach would have preserved.

Production digest preference closes a gap that base image pinning alone does not address: certainty not just about what went into building an image, but about exactly what specific, immutable artifact is actually deployed and running, which depends on capturing and propagating the build's digest through every stage of the deployment pipeline rather than trusting a tag's continued, stable meaning over time.