KubernetesLXIV · Kubernetes Supply Chain SecuritySupply chain security
Image tags vs digests — pinning for supply chain integrity
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
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:
- Build and push the image with a tag.
- Scan and sign the image using the tag.
- Pin the Pod to the digest.
- 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 forlatest.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
- A Pod uses
latestand the image is overwritten. The Pod pulls a different image on next start. The fix is to pin by digest. - 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.
- 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).
- The CI pipeline does not enforce digest pinning. The team pushes untagged images. The fix is to enforce the policy in CI.
Production patterns
- Pin every image by digest. No exceptions in production.
- Use
imagePullPolicy: IfNotPresent. The cached image is the correct image. - Automate digest updates. A tool (e.g., Renovate, Dependabot) updates the digest when a new image is available.
- Audit
latestand 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
Q1. What is the difference between a tag and a digest?
Q2. For digest-pinned images, `imagePullPolicy: Always` is recommended to ensure the latest version of the digest is always pulled.
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`).
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.