OCI Image Indexes: How One Container Tag Selects Multiple Platforms
How OCI image indexes connect platform descriptors to immutable manifests, and how runtimes select, verify, publish, and debug multi-platform images.
A multi-platform container reference does not contain one universal executable image. It normally resolves to an OCI image index: a JSON document whose descriptors point to separate manifests for Linux on AMD64, Linux on ARM64, Windows, or other platform combinations. The runtime selects one descriptor, fetches that manifest, and then downloads its configuration and layers.
This extra level explains why the same tag can run natively on different machines and why a digest copied from one inspection command may identify the index rather than the platform-specific image.
Index, manifest, configuration, and layers are distinct objects
An OCI image index uses the media type application/vnd.oci.image.index.v1+json. Its manifests array contains descriptors. Each descriptor records a media type, digest, size, and usually a platform object.
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:...",
"size": 7021,
"platform": { "os": "linux", "architecture": "amd64" }
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:...",
"size": 6988,
"platform": { "os": "linux", "architecture": "arm64", "variant": "v8" }
}
]
}
Each referenced image manifest then points to one configuration object and an ordered set of filesystem-layer descriptors. Every edge is content-addressed. Changing a platform manifest changes its digest; changing that descriptor changes the index digest.
Platform matching is metadata, not CPU emulation
The descriptor can name operating system, architecture, variant, OS version, and OS features. A runtime compares those fields with the target environment and chooses a compatible entry. The index does not translate instructions and does not prove that the binary inside a nominally matching image actually targets that architecture.
For ARM, variants such as v6, v7, or v8 can matter. For Windows, OS version constraints may participate in compatibility. Publishers should emit accurate platform metadata and test the image on the claimed platform rather than trusting build arguments.
When no descriptor matches, a runtime may report “no matching manifest” even though the tag exists. Inspect the top-level media type and every platform entry before diagnosing credentials or network access.
A tag and an index digest answer different questions
A registry tag is mutable unless policy prevents movement. Today example/app:stable may point to one index digest; tomorrow it may point to another. Pulling by the index digest freezes the complete platform menu, while pulling by one child manifest digest freezes only that platform image.
Deployment systems should record both when reproducibility matters:
- the index digest selected from the release;
- the platform tuple and child manifest digest actually executed.
That evidence distinguishes “the release tag moved” from “the same index selected a different platform” and from “nodes ran different architecture-specific builds by design.”
Signatures and attestations also need an explicit subject. Signing only each child leaves the index relationship unauthenticated. Signing only the index establishes the selected set but may not satisfy a policy engine that evaluates child manifests. Define which digests are signed and how the verifier follows descriptors.
Publishing must be atomic from the consumer’s view
A safe pipeline builds and pushes each platform manifest first, verifies that every digest is retrievable, constructs the index from those immutable digests, and moves the release tag only after the index is complete.
Do not publish the final tag to a partial index and append platforms later. Consumers can cache the incomplete digest, and a later tag movement does not repair deployments pinned to it. Use a temporary tag or direct digests during assembly.
Cross-building adds another trap: running package-manager or test commands for a foreign architecture may depend on transparent emulation. A successful build under QEMU does not prove native runtime behavior or performance. Execute smoke and integration tests on native workers for each supported platform when possible.
Debug from the top level down
When a multi-platform pull fails, follow the content graph:
- Resolve the tag and record the returned digest and media type.
- If it is an index, list platform descriptors exactly as published.
- Reproduce selection for the node’s OS, architecture, and variant.
- Fetch the selected manifest by digest.
- Verify its configuration and every layer descriptor.
- Compare the runtime’s recorded digest with the release evidence.
Registry user interfaces sometimes flatten this hierarchy and display a child size or architecture next to the index tag. Use an OCI-aware inspection tool or registry API when the distinction matters.
Verify negotiation as well as the JSON body
The registry protocol makes media type part of the contract. A client should send an Accept header listing the manifest types it understands, then compare the response Content-Type with the document’s mediaType. On a successful manifest response, Docker-Content-Digest identifies the returned object. If the request used a digest, calculate the digest of the response and require it to match rather than trusting the header alone.
This catches a class of proxy and compatibility failures that JSON inspection misses. An old registry, cache, or client may return a single Docker manifest when the publisher expected an OCI index, or may rewrite content while preserving a tag. Record the request reference, response content type, digest header, and locally verified digest before walking child descriptors.
Selection order also matters. The image-index specification says that when several descriptors satisfy the runtime requirements, the first matching entry should be used. Do not publish ambiguous duplicate platform tuples and assume every runtime will choose the same child. Treat os.version, required OS features, and CPU variant as selection inputs, while remembering that the descriptor features field is reserved rather than a place for private capability flags.
Indexes can also describe artifact collections and weakly associate with a subject for referrer discovery. That does not turn every referrer into a runnable platform image. Deployment tooling should select only descriptors with an expected image media type and platform, while supply-chain tooling follows subjects, artifact types, and annotations through a separate verified path.
An image index is a small routing document with large supply-chain consequences. Treat it as an immutable release object, validate every referenced manifest before publication, and retain the exact child chosen at runtime.
Related:
- Kubernetes ValidatingAdmissionPolicy: CEL Policy Without a Webhook
- Container Tags, Digests, SBOMs, and Provenance: Building a Verifiable Release Chain
Sources: