apiVersion: v1
kind: Pod
metadata:
name: shop
namespace: shop
spec:
containers:
- name: shop
image: ghcr.io/acme/shop:1.4.2
imagePullPolicy: IfNotPresentKubernetes graph
Inside an OCI image: tag, index, manifests, config and layers
How a registry stores a multi-platform container image, with its attestations and a signature, and how containerd walks from a tag to a running container's root filesystem.
A tag like ghcr.io/acme/shop:1.4.2 points at a tree of JSON documents and tarballs, each stored under the SHA-256 digest of its bytes. The index lists one manifest per platform. Each manifest lists a config and the layers, in the order they stack.
Signatures and SBOMs sit next to the image, not inside it, because adding anything to the image would change its digest. They point back at it with a subject field, and the registry's referrers API finds them. The article [OCI images explained](/articles/oci-images-explained-manifests-layers-media-types-and-artifacts) covers every object here in more detail.
Interactive graph
Click a resource to see what it does, or follow the setup step by step.
How it works
All 13 resources and what they do
- Pod shop-7d9f8c6b5-x2k4pv1
- Asks for
ghcr.io/acme/shop:1.4.2. Pinning@sha256:…instead would skip the tag lookup and make every node run exactly the same bytes. - Container runtime containerd
- Pulls for the kubelet. It sends an
Acceptheader listing every manifest format it understands, picks the manifest for the node's platform, downloads only the blobs its content store lacks, and checks each one against its digest and size. - Tag shop:1.4.2
- A name that points at a digest, here the index's. Tags can be moved to another digest at any time, which is why
1.4.2today may not be1.4.2tomorrow. - Image index sha256:5e1a…
- Media type
application/vnd.oci.image.index.v1+json. Lists one manifest per platform, plus attestation manifests thatdocker buildxadds. Pin this digest to keep the image multi-platform. - Image manifest linux/amd64 sha256:7d1c…
- Media type
application/vnd.oci.image.manifest.v1+json. One image for one platform: a config descriptor and three layer descriptors, bottom layer first. - Image manifest linux/arm64 sha256:e83a…
- The arm64 build, with its own config and layers. An amd64 node never downloads any of it. A node whose platform isn't in the index fails with
no match for platform in manifest. - Attestation manifest unknown/unknown sha256:0c4d…
- Added by
docker buildxfor the amd64 image. Its layers have the media typeapplication/vnd.in-toto+jsonand hold SLSA provenance and, if you asked for one, an SPDX SBOM. Theunknown/unknownplatform keeps runtimes from trying to run it. - Image config sha256:b5d2…
- Media type
application/vnd.oci.image.config.v1+json. What the runtime needs besides files: user, environment, entrypoint and working directory, plusrootfs.diff_ids, the digests of the uncompressed layers. - Layer #1 base OS, 29 MB
- A gzipped tar of the Debian base filesystem. Every image built on the same base shares this blob in the registry and this snapshot on the node, so it's usually already there.
- Layer #2 packages, 8 MB
- Only the files that
apt-get installadded or changed. A file deleted here would show up as a whiteout entry named.wh.<name>, and the bytes in layer 1 would still be shipped. - Layer #3 app binary, 14 MB
- The
COPYof the application binary. It changes on every release, so it's last: rebuilding reuses layers 1 and 2, and a node that ran the previous version downloads only this one. - Signature cosign bundle
- A separate manifest created by
cosign sign. Itssubjectpoints at the index digest, so the registry lists it underGET /v2/acme/shop/referrers/sha256:5e1a…. Admission policies such as Kyverno'sverifyImageslook it up before a Pod may start. - Snapshots overlayfs root filesystem
- containerd unpacks each layer into a snapshot on top of the one below, keyed by chain ID, and mounts them as overlay lower directories. The container gets an empty writable layer on top, which disappears with the container.
Common mistakes
exec format errorat startup almost always means the image has no manifest for the node's platform, or one built for the wrong one.crane manifest ghcr.io/acme/shop:1.4.2shows which platforms the index lists.- Pinning a single platform's manifest digest in a cluster with mixed architectures makes the Pods fail on the other nodes. Pin the index digest.
- Deleting a file in a later Dockerfile step doesn't shrink the image. The layer that added it still ships, and the later layer adds a whiteout. Remove temporary files in the same
RUNthat created them. - A signature made for one digest doesn't cover another. Re-tagging and pushing rebuilt bytes needs a new signature, and so does a manifest that a registry or a copy tool rewrote.
- With
imagePullPolicy: IfNotPresentand a moving tag, nodes that pulled1.4.2earlier keep the old bytes. Use digests, or immutable tags enforced by the registry.
The manifests you write
Everything above that is marked "You write it", in one file. The rest comes from add-ons and controllers.
Keep going
More graphs
- Deployment, Service and Ingress: how a request reaches your Pods
- Gateway API: GatewayClass, Gateway and HTTPRoute with a canary split
- PVC, StorageClass and CSI: how a cloud disk ends up in your Pod
- RBAC: how a ServiceAccount gets its permissions
- cert-manager with Let's Encrypt: Certificate, CertificateRequest, Order and Challenge
- A private CA with cert-manager and trust-manager
- Istio traffic management: Gateway, VirtualService, DestinationRule and policies
- Flux dependencies: Git and OCI sources, Kustomizations and HelmReleases
- Argo CD: ApplicationSet, AppProject and the Applications it generates
- OCI artifacts: Helm charts, Flux artifacts and image volumes