Build a Multi-Platform Image with docker manifest

You have an amd64 image and an arm64 image, and you want one tag that serves both with docker manifest. This guide combines the pushed images into a manifest list, checks the platforms and pushes it. Allow 15 to 30 minutes if the images and registry credentials are ready.

The examples use Docker 29.8.1, installed from the docker-ce-cli package on this machine.

Tip: If you only need to verify a published image, stop after step 2. Confirm the repository and tag before the push in step 5.

1. Check the command and your image references

It is experimental. The installed CLI and the current Docker documentation both mark docker manifest as experimental, so its behaviour may change.

It talks to a registry. It does not ask the Docker Engine for images. That is why every source image must already exist in the registry.

$ docker --version
Docker version 29.8.1, build 4a63305
$ docker manifest --help
Usage:  docker manifest COMMAND

Use fully qualified names for a private registry, such as registry.example.com/team/widget:linux-amd64. Keep the final list name in the same registry, for example registry.example.com/team/widget:stable. A list created locally is not automatically used by docker pull; it must be pushed.

2. Inspect a manifest without changing it

Inspect a public or authenticated registry reference first. This is read-only and catches a wrong tag before you build a list around it.

$ docker manifest inspect hello-world
{
   "schemaVersion": 2,
   "mediaType": "application/vnd.oci.image.index.v1+json",
   "manifests": [
      {
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      }
   ]
}

The real response contains digests and more platform entries. Add --verbose when you need the resolved reference, digest, layers and platform details. The result is JSON, so a non-zero exit status or an authentication error means there is nothing safe to publish yet.

$ docker manifest inspect --verbose registry.example.com/team/widget:linux-amd64
$ printf 'exit status: %s\n' "$?"
exit status: 0

3. Create a local manifest list

Replace every placeholder below with an image that is already in the target registry. The command queries the registry and stores the list locally. It does not upload the list.

$ docker manifest create registry.example.com/team/widget:stable \
    registry.example.com/team/widget:linux-amd64 \
    registry.example.com/team/widget:linux-arm64
Created manifest list registry.example.com/team/widget:stable

Use --amend if the named local list already exists and you deliberately want to replace or extend its entries. Do not use it to paper over an incorrectly tagged image. Inspect the local list before continuing:

$ docker manifest inspect registry.example.com/team/widget:stable
$ docker manifest inspect --verbose registry.example.com/team/widget:stable

If creation fails, check these four things:

Warning: For an HTTP or self-signed private registry, the relevant transactions need --insecure, for example docker manifest create --insecure .... Treat that as a registry security decision, not a routine workaround.

4. Correct platform metadata only when required

Docker normally derives platform information from each image. Use annotate only when the source metadata is wrong or incomplete and you have confirmed the correct values. The changes apply to the local list.

$ docker manifest annotate \
    registry.example.com/team/widget:stable \
    registry.example.com/team/widget:linux-arm64 \
    --arch arm64 \
    --os linux

The available fields are architecture, operating system, operating-system features, operating-system version and architecture variant. Re-run verbose inspection and check that each entry has the expected platform before pushing.

Warning: A wrong annotation can make a client select an unusable image.

5. Push the list and record its digest

Warning: This publishes a tag in the registry and can change what clients pull. Confirm the repository, tag, source image digests and registry permissions first. No sudo is normally needed; Docker registry authentication is separate from local root privileges.

$ docker manifest push registry.example.com/team/widget:stable
Pushed manifest registry.example.com/team/widget@sha256:SOURCE_DIGEST with digest: sha256:LIST_DIGEST
sha256:LIST_DIGEST

The digest values are different on each real push, so treat the output above as a shape, not a literal result. Save the final list digest in your release record, then verify the remote object:

$ docker manifest inspect registry.example.com/team/widget:stable
$ docker manifest inspect --verbose registry.example.com/team/widget:stable

6. Remove an abandoned local list

If you created the wrong local list and have not pushed it, remove that local object with:

$ docker manifest rm registry.example.com/team/widget:stable

Warning: This does not undo a push. A published tag needs the registry's normal tag-management or deletion process, which is outside docker manifest rm.

If a push failed, leave the local list in place until you have diagnosed the failure, or remove it and recreate it deliberately.

Done means