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.
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.
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
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.
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.
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
--insecure on push. Use it only for a registry where that transport is explicitly intended.--purge. Use it only if you also want the local copy removed after a successful push. It does not remove the registry tag.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.
docker manifest inspect.rm and --purge affect only local manifest-list state.