An image index is a manifest that points to other manifests, effectively creating a list of images that are compatible with different architectures and operating systems.

Let’s see it in action. Imagine you’re building an image for multiple architectures using BuildKit. You’d typically configure your build to target different platforms.

# Dockerfile
FROM alpine:latest
RUN echo "Hello from $(uname -m)"

Then, you’d build it for several architectures:

# Build for amd64
docker buildx build --platform linux/amd64 -t myrepo/myapp:amd64 .

# Build for arm64
docker buildx build --platform linux/arm64 -t myrepo/myapp:arm64 .

When you push these, BuildKit doesn’t just push individual images. It creates an index that bundles them. You can inspect this index:

# Inspect the index manifest
docker manifest inspect myrepo/myapp:latest

The output will look something like this, showing a mediaType of application/vnd.oci.image.index.v1+json and manifests pointing to the individual image manifests for each platform:

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 1234,
      "digest": "sha256:digest-for-amd64",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 5678,
      "digest": "sha256:digest-for-arm64",
      "platform": {
        "architecture": "arm64",
        "os": "linux"
      }
    }
  ]
}

This index is what myrepo/myapp:latest actually points to. When you pull myrepo/myapp:latest, your container runtime (like Docker or containerd) looks at the index, checks your system’s architecture, and pulls the correct, compatible image manifest and its layers. This is how a single tag can serve multiple underlying images.

The problem this solves is the need for a single, universal tag that can represent an image capable of running on diverse hardware and operating systems. Before multi-platform images and indexes, you’d have to manage separate tags like myapp:latest-amd64, myapp:latest-arm64, and so on, forcing users to know which tag was appropriate for their environment. The index, acting as a dispatcher, abstracts this complexity away.

Internally, BuildKit leverages the OCI (Open Container Initiative) Image Format specifications. An index manifest is itself a JSON file. It has a mediaType of application/vnd.oci.image.index.v1+json. Its core component is the manifests array, where each element describes a single image manifest suitable for a specific platform (defined by architecture, os, and variant). Each element also includes the digest and size of the referenced image manifest.

The platform field is crucial. It allows you to specify architecture (e.g., amd64, arm64, riscv64), os (e.g., linux, windows), and even os.version, os.features, and variant (e.g., v7l for ARMv7 hard float). When a container runtime pulls an image tagged with an index, it consults this platform information to select the most appropriate manifest from the index for the host system.

The default behavior when you build and push a multi-platform image with docker buildx is to create an index and tag it with the most general tag (like :latest). If you don’t explicitly specify a platform for the final tag when pushing, BuildKit will create an index for you, consolidating all the platforms you built.

When you build an image with buildx and push it to a registry, BuildKit automatically generates an index manifest if you’ve built for multiple platforms. This index manifest is then pushed to the registry with the tag you specified (e.g., myrepo/myapp:latest). The individual image manifests and their layers are pushed as separate artifacts, referenced by the index. This means the registry stores multiple distinct image configurations and layers, but exposes them under a single, unified tag via the index.

The most surprising true thing about image indexes is that the latest tag itself doesn’t inherently mean "the newest version." It’s simply a convention, and for multi-platform images, it means "the index manifest that points to the latest compatible images for your platform." If you push a new version of your image for only one architecture, the latest tag (the index) will be updated to point to that new manifest, but older, compatible manifests for other architectures will still be present and accessible if the index is consulted by a runtime of that architecture.

The next concept to explore is how to manage image layers efficiently when dealing with multi-platform images, especially concerning deduplication and storage.

Want structured learning?

Take the full Buildkit course →