Skip to main content
RunBook Academy

KubernetesLXIV · Kubernetes Supply Chain SecuritySupply chain security

Image tags vs digests — pinning for supply chain integrity

Advanced⏱ ~13 minkubectl

What you'll learn

  • Explain the difference between image tags and digests
  • Pin a Pod's image by digest for supply chain integrity
  • Identify the production failure modes (mutable tags, `latest`, cache poisoning)
  • Use image policy admission to enforce digest pinning

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

A container image is identified by either a tag (v1.0) or a digest (sha256:abc...). Tags are mutable — the same tag can refer to different digests over time. Digests are immutable — the same digest always refers to the same image. Pinning by digest is the foundation of supply chain integrity; without it, every other control (vulnerability scanning, signature verification) is on a moving target.

The tag model

Tags are mutable pointers to digests:

flowchart LR
    A[v1.0 tag] --> D1[sha256:abc]
    A --> D2[sha256:def]
    D1 --> I1[Image layer set 1]
    D2 --> I2[Image layer set 2]

When you docker push an image with v1.0, the registry updates the v1.0 tag to point at the new digest. Anyone who pulls v1.0 later gets the new digest. The old digest may still exist (until garbage collected) but is no longer referenced by v1.0.

The latest tag is the production anti-pattern: latest always points at the most recent push; an attacker who pushes a malicious image overwrites it.

The digest model

Digests are content-addressed:

flowchart LR
    A[Image content] --> H[SHA-256 hash]
    H --> D[sha256:abc]
    D --> B[Tag v1.0]
    D --> C[Tag stable]

The digest is computed from the image’s manifest, which references the config and the layers. Two images with the same content have the same digest; two images with different content have different digests. The digest is immutable: once computed, it never changes.

# Get the digest of a local image
docker images --digests
# myapp  v1.0  sha256:abc123...  2026-08-16

# Pull by digest
docker pull myapp@sha256:abc123...

The pull by digest fetches exactly the image that was pushed; no tag is consulted. This is the production primitive.

Pinning by digest in a Pod

A Pod can reference an image by tag or by digest:

spec:
  containers:
  - name: api
    image: myapp:v1.0                      # by tag (mutable)
    image: myapp@sha256:abc123...          # by digest (immutable)

The digest form pins the exact image. The Pod always pulls the same content; the registry cannot redirect the pull to a different image.

The convention:

  1. Build and push the image with a tag.
  2. Scan and sign the image using the tag.
  3. Pin the Pod to the digest.
  4. Rotate the Pod when a new image is needed.
# Build and push
docker build -t myapp:v1.0 .
docker push myapp:v1.0

# Get the digest
DIGEST=$(docker inspect myapp:v1.0 --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
# sha256:abc123...

# Update the Pod to use the digest
kubectl set image deployment/api api=myapp@sha256:abc123...

The ImagePullPolicy

The Pod’s imagePullPolicy field controls when the image is pulled:

  • Always — pull on every Pod start. The conventional default for latest.
  • IfNotPresent — pull only if not cached. The conventional default for digest-pinned images.
  • Never — never pull; the image must be on the node.

For digest-pinned images, IfNotPresent is safe: the digest is immutable, so the cached image is the correct image.

spec:
  containers:
  - name: api
    image: myapp@sha256:abc123...
    imagePullPolicy: IfNotPresent

Enforcing digest pinning

A cluster can enforce digest pinning via admission:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: require-digest
spec:
  failurePolicy: Fail
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
  validations:
  - expression: "object.spec.containers.all(c, c.image.contains('@sha256:'))"
    message: "all container images must be pinned by digest"

The policy rejects any Pod whose image is not pinned by digest. The CI pipeline fails on the policy violation.

Production failure modes

  1. A Pod uses latest and the image is overwritten. The Pod pulls a different image on next start. The fix is to pin by digest.
  2. A Pod uses a tag and the tag is updated. The Pod pulls the new image on next start. The fix is to pin by digest.
  3. A Pod uses a digest that is garbage collected. The kubelet cannot pull; the Pod fails. The fix is to ensure the registry retains old digests (or to update the Pod to a current digest).
  4. The CI pipeline does not enforce digest pinning. The team pushes untagged images. The fix is to enforce the policy in CI.

Production patterns

  1. Pin every image by digest. No exceptions in production.
  2. Use imagePullPolicy: IfNotPresent. The cached image is the correct image.
  3. Automate digest updates. A tool (e.g., Renovate, Dependabot) updates the digest when a new image is available.
  4. Audit latest and tag-pinned images. A quarterly audit flags every Pod that does not pin by digest.

Cross-course references

  • The Linux course covers the OCI image format and content addressing.
  • The Observability course covers the audit log entries for image pulls.

Quiz

Knowledge check · 4 questions

  1. Q1. What is the difference between a tag and a digest?

  2. Q2. For digest-pinned images, `imagePullPolicy: Always` is recommended to ensure the latest version of the digest is always pulled.

  3. Q3. Your cluster has a Deployment with `image: myapp:latest`. An attacker compromises the CI pipeline and pushes a malicious image with `latest`. All Deployments using `myapp:latest` pull the malicious image on next rollout. Walk the response.

    The cluster has 30 Deployments using `myapp:latest`. The CI pipeline was compromised; the attacker pushed `myapp:latest` with a backdoor. All 30 Deployments will pull the malicious image on next rollout (or on node restart if `imagePullPolicy: Always`).

  4. Q4. How do you pin a Pod's image by digest, and what `imagePullPolicy` should you use?

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

Production discipline

Tag vs digest is the foundation of supply chain integrity. A defensible image policy pins every image by digest, uses imagePullPolicy: IfNotPresent, automates digest updates via CI, and enforces digest pinning with a ValidatingAdmissionPolicy. A cluster whose images are all digest-pinned has a supply chain that is auditable; a cluster whose images use latest or mutable tags has a supply chain that is not.