Fix Image Platforms with docker manifest annotate
Your ARM image is listed under the wrong architecture, and docker manifest annotate is the fix you need before you push. This guide ends with a local manifest list whose platform metadata is explicit and checked. It takes about 15 minutes, and the examples use Docker CLI 29.8.1.
The route
Jump straight to the step you need, or tick off Done means at the end.
You need Docker CLI access, plus registry credentials if you intend to create or push a real list. The annotation edits only Docker's local manifest-list copy. It does not rebuild an image, change the image layers, or alter the Docker daemon.
Tip
This guide covers platform fields: operating system, architecture, OS version, OS features and architecture variant. It does not cover OCI key-value annotations, which are a different feature in newer build workflows.
1. Confirm the command available on this host
Check the client version and the command's own option list:
$ docker version --format '{{.Client.Version}}'
29.8.1
$ docker manifest annotate --help
Usage: docker manifest annotate [OPTIONS] MANIFEST_LIST MANIFEST
Add additional information to a local image manifest
The installed manual marks this command as an addition to a local image manifest. Docker also labels the manifest command family experimental. Experimental behaviour can change between releases, so keep the client version in deployment notes when this command is part of a release process.
No root privilege is normally needed. The command talks to Docker's local manifest-list store, not to the engine, and it never modifies a running container. If your Docker installation uses a protected configuration directory, fix that access issue according to your local policy rather than adding sudo automatically.
2. Create or select the local manifest list
annotate needs two positional arguments. The first is the name of the local manifest list. The second identifies one image manifest already included in that list. Create the list first when you do not already have one:
$ docker manifest create registry.example.invalid/acme/widget:2026.09 \
registry.example.invalid/acme/widget-amd64:2026.09 \
registry.example.invalid/acme/widget-arm64:2026.09
Replace every example registry name with your real registry and use image references that actually exist there. Docker queries the registry while creating the local list. A successful create leaves a local copy for inspection, annotation and a later push. It does not publish the list by itself.
If the list already exists locally, do not recreate it just to change one entry. Use docker manifest inspect first:
$ docker manifest inspect --verbose registry.example.invalid/acme/widget:2026.09
Look for the descriptor whose digest or image reference corresponds to the child you plan to annotate. The spelling and tag in the second positional argument must identify that child, not another list or an unrelated image.
Checkpoint
You have written down the manifest-list name, the child-manifest name and the intended platform. A typo fails clearly, but picking the wrong child produces a valid yet misleading list.
3. Annotate a child manifest
Suppose the ARM child was recorded without the architecture information you expect. Set the architecture with --arch:
$ docker manifest annotate \
registry.example.invalid/acme/widget:2026.09 \
registry.example.invalid/acme/widget-arm64:2026.09 \
--os linux \
--arch arm64
The installed manual does not describe any output for this command. A return to the shell counts as success only after you have checked the stored list in step 4. The annotation applies to the local copy named by the first argument; it does not change the child image in the registry.
Use the other options only when the child image really needs them:
--ossets the operating system, such aslinuxorwindows.--archsets the CPU architecture, such asamd64,armorarm64.--variantsets an architecture variant, for example a variant required by an ARM image.--os-versionrecords the operating-system version when the platform needs that distinction.--os-featuresrecords operating-system feature values. The installed option is a string list, so pass the option once for each value you need.
Warning
Do not copy --variant from a different architecture, because a variant belongs to the architecture it describes. Do not invent an OS version or feature to fill a field either. Platform metadata influences which image a client selects, so inaccurate values can make a supported machine pull the wrong child.
4. Verify the stored platform data
Inspect the local list again, this time requesting verbose output:
$ docker manifest inspect --verbose \
registry.example.invalid/acme/widget:2026.09
Find the annotated child and check its platform object. For the command above, the relevant part should identify the child as Linux on ARM64:
{
"platform": {
"architecture": "arm64",
"os": "linux"
}
}
The complete output contains digests and other descriptor information, and its formatting can vary by client release. Check the values rather than comparing the entire output as text. If the child is absent, stop and correct the manifest-list or child reference. Do not push a list you have not matched to its intended image.
For an ARM variant, verify the additional field as well:
$ docker manifest annotate \
registry.example.invalid/acme/widget:2026.09 \
registry.example.invalid/acme/widget-arm:2026.09 \
--os linux --arch arm --variant v7
$ docker manifest inspect --verbose registry.example.invalid/acme/widget:2026.09
Tip
Only use v7 if the image was built for that ARM variant. The value is an example, not a safe default for every ARM image.
5. Push only after the check
Once every child has the correct platform metadata, publish the local list:
$ docker manifest push registry.example.invalid/acme/widget:2026.09
This is the first command in the guide that publishes registry state. It normally requires registry authentication and permission to update the tag.
Warning
Do not use --insecure as a troubleshooting shortcut. The Docker documentation reserves that option for registries where ignoring normal certificate requirements is an explicit, understood decision. Prefer a correctly configured TLS certificate.
The push command can also remove the local list with --purge. That is optional and destructive to the local copy, so leave it off until the remote push has been checked. If you need to amend the list later, retaining the local copy is useful:
$ docker manifest inspect --verbose \
registry.example.invalid/acme/widget:2026.09
Recovery
To undo an annotation before pushing, annotate the same child again with the correct values and verify it. To recover after an incorrect list has already been pushed, correct the local copy, push the corrected list to the intended tag, and verify the remote result with an inspection command. Consider a new release tag when your release process does not permit replacing an existing tag.
6. Fix the common failures
- Manifest list not found locally. Create it, or use the exact local name returned by the create step. A registry reference can exist remotely while no local copy exists for
annotateto edit. - Child not found. Compare the second argument with the references used by
docker manifest create. The child may have been recorded by digest or under a different tag. Inspect the list and choose the descriptor you actually intend to change. - Inspection still shows the old platform. Check that you annotated the same list name and child name that you inspected. Also check the option spelling: the installed command uses
--os-featuresand--os-version, not similarly named build or OCI annotation options. - Push fails. Separate local annotation from registry access. A certificate, authentication or permission error is not fixed by changing platform fields. Repair the registry connection or credentials, then inspect the local list again before retrying. Avoid repeating a push blindly when the tag may have moved.
Done means
- Version recorded. You noted the Docker client version and understood the experimental status.
- Right target. The manifest-list name and child manifest were matched before annotation.
- Honest values. Only platform values supported by the child image were supplied.
- Verified.
docker manifest inspect --verboseshowed the expected OS, architecture and any required variant. - Pushed last. The list went out only after verification, and the local copy stayed unless removal was deliberate.