Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXVII · Dependency PinningImagePinning

Container image pinning — digests over tags

Advanced⏱ ~24 mingit

What you'll learn

  • Pin a container image by digest rather than by tag and verify the digest format
  • Resolve a tag to its digest at pin time using the registry API or docker pull
  • Promote a single digest across environments to guarantee the same bytes in dev, staging, and prod
  • Establish a re-pin workflow that treats digest bumps as supply-chain changes

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

A container tag is a publisher-chosen string that resolves to whatever the publisher re-tags. A container digest is a SHA-256 content hash that resolves to fixed bytes forever. Production pipelines pin by digest, not by tag; the digest is the only form that survives the publisher’s choices. The pin is recorded in the manifest, the digest is verified at pull time, and the same digest is promoted across every environment so that dev, staging, and production run the same bytes.

Tags are mutable; digests are content addresses

The container registries serve two reference forms: a tag and a digest. A tag is a string the publisher writes; the publisher can move the tag, republish under the same tag, and delete the previous artifact without notice. A digest is the hash of the image’s content; the publisher cannot move a digest without changing the bytes.

# Mutable - tag can move
FROM nginx:1.25
# Immutable - digest is a content hash
FROM nginx@sha256:2cd3c2f3c4f6c4f3e3a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9

The digest form pins the bytes. The pull request that introduces the digest contains the digest, the tag, the upstream release notes, and the review notes; the digest is the workflow’s promise to the runtime to pull exactly those bytes.

flowchart LR
    A["Tag 1.25"] --> B["Pointer"]
    B["Pointer"] -. "publisher re-tags" .-> C["Different bytes today"]
    D["Digest sha256"] --> E["Bytes B1"]
    E["Bytes B1"] --> F["Same bytes forever"]

Resolving a tag to its digest at pin time

The pin is performed at a deliberate moment, under review. The resolution step turns the tag the publisher chose into the digest the publisher cannot move.

# Pull the image to populate the local cache
docker pull nginx:1.25
# Inspect the local cache and extract the digest
NGINX_DIGEST=$(docker images --no-trunc nginx:1.25 | awk 'NR==2 {print $3}')
echo "${NGINX_DIGEST}"
# sha256:2cd3c2f3c4f6c4f3e3a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9

The docker images --no-trunc command prints the full digest, not the truncated :alpine shorthand the registry shows by default. The full digest is what goes into the manifest.

The same resolution is available directly from the registry API without a local pull. The OCI distribution spec exposes the digest on the manifest endpoint; the digest is the same hash the runtime verifies at pull time.

The pull-time verification

The container runtime verifies the digest at pull time. The runtime computes the SHA-256 of the manifest it downloads and compares it to the digest the manifest declares; a mismatch fails the pull. The verification is what turns the digest into a control. A pipeline that records a digest but uses a tag at pull time is a pipeline that has the data but not the control.

# Kubernetes manifest - digest-pinned
spec:
  containers:
    - name: app
      image: nginx@sha256:2cd3c2f3c4f6c4f3e3a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9
      imagePullPolicy: IfNotPresent

The imagePullPolicy: IfNotPresent is a performance hint, not a security control; the digest is the security control. The runtime verifies the digest regardless of the pull policy.

verifies the digest regardless of the pull policy.

Image-signing and digest pinning together

A digest pins bytes; a signature binds bytes to a publisher. The two controls together establish that the bytes the team reviewed are the bytes the runtime runs, and that the bytes came from the publisher the team trusts. Sigstore cosign, Docker Content Trust (Notary), and the OCI referrers API all sign digests; the signature travels with the digest and is verified at pull time.

# Verify a digest-pinned image is signed by a trusted publisher
cosign verify --key cosign.pub nginx@sha256:2cd3c2f3c4f6c4f3e3a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9

A digest without a signature pins bytes the publisher could swap by republishing a new manifest under the same tag; the runtime would still see a valid digest, but the digest would point at the publisher’s new bytes, not the team’s review. The signature is what binds the digest to a publisher identity; without it, the digest is content addressing without identity.

Re-pinning as a deliberate upgrade

A digest is a freeze. The freeze must end deliberately. The re-pin workflow is the same five-step ceremony as the original pin: identify the upgrade, resolve the new digest, review the diff between the old bytes and the new bytes, update the manifest in a pull request, and subscribe to upstream advisories. A digest bump is a supply-chain change; a Dependabot-equivalent tool can open the PR, but the team still performs the review.

Production discipline

  1. Pin every base image, runtime image, and init container by digest. No tags. No latest.
  2. Resolve the digest at pin time. Document the tag, the upstream release, and the resolved digest in the PR.
  3. Promote the same digest across every environment. Dev, staging, and prod carry the same digest string.
  4. Verify the signature. A digest without a signature is content addressing without identity.
  5. Re-pin deliberately. A digest bump is a supply-chain change; route every re-pin through review.

Cross-course references

  • Git, CI/CD & GitOps — Part LXVII-01 (Pinning Discipline) defines the discipline that motivates digest pinning.
  • Git, CI/CD & GitOps — Part LIII-06 (Image Signing) covers the signature layer that digest pinning leaves open.
  • Git, CI/CD & GitOps — Part LXII-04 (Policy) covers the policy layer (Kyverno, Conftest) that enforces digest pinning at the cluster.
  • Linux for Production Sysadmins — Part XXIV (Containers) covers the runtime that consumes the digest-pinned image.

Quiz

Knowledge check · 4 questions

  1. Q1. Why does production-grade container pinning reference the image by digest rather than by tag?

  2. Q2. A digest-pinned image without a signature pins the bytes the publisher could swap by republishing a new manifest under the same tag.

  3. Q3. Explain why a team that pins by digest in production but pulls by tag in staging is testing different bytes than it ships.

  4. Q4. Identify the gap in the team's image-pinning practice and the rule that closes it.

    Team T maintains 40 services. Production Kubernetes manifests pin the base image by digest. Staging manifests pin by tag. The CI pipeline builds a new image, tags it with the build number, and pushes to the registry. A security advisory is published for the base image's underlying library. The team must rebuild every service to pick up the patched base image.

Passing score: 75%. Answers are checked in this browser.