For quite some time now, the docker community has been grappling with the complexity of supporting multiple operating systems and architectures. The following are two key issues that have arisen as a result of this diversity:

  • New docker users tend to assume that if they do a docker run of an image, that it will work on their platform, and have no idea how to interpret the failure. Sometimes, the error message is exec format error, which is not intuitive. However, what a user may see when using the wrong type of image also depends on the image that is being run.
  • Projects that do support multiple platforms have resorted to using image names to indicate which platform an image was built for. For example, my-cool-app-ppc64le:latest; or creating a Docker Hub namespace for each architecture, like ppc64le/my-cool-app.

To alleviate these issues, the docker community has come up with what is currently called a manifest list, also nicknamed a multi-arch image, or fat manifest. An individual manifest describes the contents of one image, and a manifest list lets you group multiple images together. After the creation of a manifest list, anyone (from the project owners to their user-base) can use the manifest list name where they once used an individual image name. When a user does a docker pull or docker run, the docker engine does the work of selecting which image to pull based on the operating system and architecture on which it is running. As a result, a project’s users no longer have to worry about finding the name of the image that will work for their platform. The project can have a single name “my-cool-app:latest” to put in blog posts, documentation, and so on. This makes the user experience much more pleasant and intuitive.

The community was quick to create tooling around the manifest list before docker decided on how to best organize the UI. Because of all the work that was done by members of the community, the usage for such a tool began to become clear. There is currently a standalone tool maintained by Phil Estes from IBM which most people have been using to create multi-arch images — but the community also began to ask more and more for the ability to make multi-arch images using docker’s tooling. As a result, the manifest sub-command was born. It is still currently in the PR review process, but can be easily used to create a multi-arch image and push it to any docker registry. Update: The PR has been merged, and docker manifest is now included in the CLI as experimental. It is also included in Docker for Mac Edge.

Here is how to use the the new docker cli sub-command to create manifest lists.

A little terminology first:

  • Manifest – Describes a single image.
  • Manifest list – Groups multiple images; can be thought of as a list of pointers; must exist on a docker registry.
  • Reference – A name for an image or manifest list (for example,
  • Tag – The portion of the reference after the colon. This is not required. However, you can use it to version a reference. For example, app:v1. latest is the default tag.
  • Digest – the hash (currently sha256) of the image contents of a docker image or manifest list’s individual digests.

When working with multi-arch images, you can use an untagged, tagged, or a digested reference (for example, my-cool-app@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2) for image names.

There are two ways to create a manifest list. You can interactively create, annotate (optional), and push a manifest list from your local host by using the following sequence of commands:

$ docker manifest create new-list-ref-name image-ref [image-ref...]
$ docker manifest annotate new-list-ref-name image-ref --os linux --arch arm
$ docker manifest push new-list-ref-name

This will store some metatdata locally until you do the final push step to the registry, at which point the manifest list will be usable.

The first parameter when working with multi-arch images is always the manifest list name that you are going to give to users. The following image-ref parameters are the pre-built, platform-specific images you have previously pushed to a registry.

Note: This is an important detail to understand. Pushing a multi-arch image to a registry does not push the image layers. It only pushes a list of pointers to accessible images. This is why it is better to think of a multi-arch image as what it really is: a manifest list. Project maintainers might be the only ones who need to understand this distinction. End users of the images do not need to be concerned with the difference since the goal of a manifest list is to be transparent to the user.

If you would like to create a multi-arch image for your app or project, you can simply recompile the docker cli and replace your current cli binary (typically /usr/bin/docker — NOT /usr/bin/dockerd), or use the new cli straight from the build dir of the docker project.



Update #2: This PR has been merged into docker, and is experimental. See the docker cli docs for usage (and don’t use the older version from the clnperez repo) and for how to enable an experimental cli feature.

Update: There is a release created that does not have the manifest subcommand marked as experimental. Simply download the binary for your architecture from

For example:

$ sudo cp /usr/bin/docker /usr/bin/docker-cli.bak
$ sudo curl -fsSL -o /usr/bin/docker

If you would like to compile in the experimental requirement:

$ git clone --no-checkout && git checkout da86425e9f02a99659aeb8ecb8a93d2ee8b31e21

Build using the project’s Dockerfile if you are on an x86 system:

$ make -f docker.Makefile binary

To build the binary for all supported platforms (still on x86):

$ make -f docker.Makefile cross

The binaries (if you ran make cross there will be more than one) will be found in the build/ directory:

$ ls build/
docker  docker-darwin-amd64  docker-linux-amd64  docker-linux-arm  docker-linux-ppc64le  docker-windows-amd64

You can use these binaries simply as:

$ ./build/docker manifest --help

Now, you can follow the steps above (either the interactive or yaml approach) to push your multi-arch images to the registry of your choice.

Note: For insecure registries (such as a local one used for testing), if you are building and using the CI PR, ensure that you add the registry to your local docker config file.

> cat ~/.docker/config.json
"insecure-registries" : [""]

Here is an example of using the interactive approach to creating a multi-arch image:

$ docker manifest create awesome_company/mycool-app:v1 awesome_company/mycool-app-x86:v1 \
awesome_company/mycool-app-s390x:v1 \
awesome_company/mycool-app-ppc64le:v1 \
$ docker manifest annotate awesome_company/mycool-app:v1 awesome_company/mycool-app-armhf:v1 --os linux --arch arm
$ docker manifest push awesome_company/mycool-app:v1

After you have pushed your manifest list to a registry, you use it just as you would have previously used an image name.

A user on an x86 system:

$ docker run awesome_company/mycool-app:v1 echo hi

This command pulls and runs only the x86 image layers that your manifest list points to, and the user will have no awareness that ‘awesome_company/mycool-app:v1’ was a manifest list instead of an image name.

If you are using a POWER system, or any other platform that you included in your manifest list, the command is exactly the same:

$ docker run awesome_company/mycool-app:v1 echo hi

As you can see, multi-arch images (also knows as manifest lists) greatly improve the end-user experience, with just a little extra work from the project maintainers.


If you are interested in building the base images for other architectures, watch our presentation from DockerCon 2017 here: FROM ARM to z: Building, Shipping, and Running a Multi-platform Docker Swarm

For POWER development resources, several options tailored to varying focus-areas can be found here:

You can request z VMs using the LinuxOne Community Cloud here: (requires registration)

18 comments on"Create and use multi-architecture docker images"

  1. […] See for more information on multi-architecture Docker images. […]

  2. Using a manifest file does not appear to work anymore.
    $ ./build/docker manifest push -f ./annotated-manifests.yaml
    unknown shorthand flag: ‘f’ in -f
    See ‘docker manifest push –help’.
    $ ./build/docker manifest push –help

    Usage: docker manifest push [OPTIONS] MANIFEST_LIST

    Push a manifest list to a repository

    –help Print usage
    –insecure Allow push to an insecure registry
    -p, –purge Remove the local manifest list after push

    • Christy Perez October 11, 2017

      Hi Mark. Yes, the maintainer asked that I remove that support and re-add it as a follow-on PR. Sorry about that. I will update the blog post to reflect the current PR state.

  3. […] Create and use multi-architecture docker images […]

  4. […] Create and use multi-architecture docker images […]

  5. On my Pine64 Rpi which has aarch64 architecture, I pulled from Git the ‘cli’ project , I then tried to run the ‘make -f docker.Makefile cross’ , but I received the following error,

    failed to register layer: Error processing tar file(gzip: invalid checksum):
    docker.Makefile:27: recipe for target ‘build_cross_image’ failed
    make: *** [build_cross_image] Error 1

    How can I fix it?

    • Christy Perez January 05, 2018

      Ah, you can’t do the cross-build on an arm device. The dev dockerfile pulls the golang:1.8.5-alpine3.6 image, which is for x86. Try this on your dev box and then move it over to your RPi. Or you can try the build natively by running just make binary.

      • Thank you for your swift reply. I should have mentioned earlier, that I am trying to build a docker swarm with only Rpi’s – two Pine64 , one arm64 and one arm32.
        Is docker manifest feasible on only Rpis ?

  6. Leland Sindt January 11, 2018

    First, a thank you… I saw the “from arm to z” talk at Dockercon 17 in Austin. At the time it simply “sounded interesting” but recently the topics covered have become invaluable to my current project.

    It appears that docker is using/supporting multiple architectures for their alpine image via a manifest list:

    docker manifest inspect alpine |grep architecture
    “architecture”: “amd64”,
    “architecture”: “arm”,
    “architecture”: “arm64”,
    “architecture”: “386”,
    “architecture”: “ppc64le”,
    “architecture”: “s390x”,

    and its working as expected…

    pi@raspberrypi: ~ $ docker image pull alpine && docker image inspect alpine |grep Architecture
    Using default tag: latest
    latest: Pulling from library/alpine
    95d54dd4bdad: Pull complete
    72bf7d76c392: Pull complete
    Digest: sha256:7df6db5aa61ae9480f52f0b3a06a140ab98d427f86d8d5de0bedab9b8df6b1c0
    Status: Downloaded newer image for alpine:latest
    “Architecture”: “arm”,

    leland@amd64: ~ $ docker image pull alpine && docker image inspect alpine |grep Architecture
    Using default tag: latest
    latest: Pulling from library/alpine
    ff3a5c916c92: Pull complete
    Digest: sha256:7df6db5aa61ae9480f52f0b3a06a140ab98d427f86d8d5de0bedab9b8df6b1c0
    Status: Downloaded newer image for alpine:latest
    “Architecture”: “amd64”,

    Now for the fun part… I need to build an arm image on my amd64 system using binfmt_misc/qemu….

    Is there a way to instruct docker to use the arm Image/Architecture when running in an amd64 environment?

    • Christy Norman March 07, 2018

      Yes, for building, you can use a multi-stage build and copy in the qemu static binary, then use that. For running you can just bind-mount the arm qemu static binary using the -v. Just make sure you’re bind-mounting it in to where your binfmt misc registration expect for it to be.

  7. Thank you for your post.

    I am giving multi-arch repo a try.

    In my case, I use tag to seperate architectures. Because on docker hub, it is a private account with private repo, So I do have only one namespace and limited repos according to my plan.

    There are two images, repo:latest:arm32 and repo:latest-amd64. I want to link them to repo:latest.

    the command `docker manifest create repo:latest repo:latest:arm32 repo:latest-amd64` works fine at the first time.

    Later, I rebuilt/updated repo:latest:arm32 and repo:latest-amd64 on different building machines. I found repo:latest did not link to the newer repo:latest:arm32 and repo:latest-amd64.

    How could I upgrade the manifest? I have tried the `create` option with `–amend`, nothing changed. The cli tools look like a one-way approach to me.

    Also How could I delete items in the manifest list?

    • Christy Norman March 07, 2018

      you have to just recreate and re-push to pick up any new images. you can’t delete individual items in the list except by re-pushing without them.

  8. After executing the manifest commands below I am getting authorization errors on push.

    docker manifest create consoleapp ivanfarkas/consoleapp-windows ivanfarkas/consoleapp-nano
    docker manifest push consoleapp

    failed to mount blob ivanfarkas/consoleapp-nano@sha256:dffe0f7c5debe5e1f879bbbfe4fb2cb04bdf7b0820af9558be9b4ff9d951f181 to errors:
    denied: requested access to the resource is denied
    unauthorized: authentication required

    • Christy Norman March 07, 2018

      You need to do the create as ivanfarkas/consoleapp and then push ivanfarkas/consoleapp.

  9. What are the manifest properties of an Alpine image?

    • Christy Norman March 07, 2018

      They’re the same as any other. Just different layers. You can do a manifest inspect of an image and see what layers are there. Also note that there’s a -v option for inspect that will give you additional arch detail.

  10. Hi, I want to push to my private repository. docker pull and docker push of the images work without a problem. I installed the tls as trustful on my system. As a try I pulled the hello-world images for the architectures

    When I want to create a manifest out of it I always get a “denied: requested access to the resource is denied”

    docker manifest create –insecure gernot-pi3-brain:30500/ghoben/multiarch-hello-world ghoben/hello-world-linux-amd64:latest ghoben/hello-world-linux-arm:latest
    denied: requested access to the resource is denied
    unauthorized: authentication required

    same when I only want to use it local:
    docker manifest create –insecure ghoben/multiarch-hello-world ghoben/hello-world-linux-amd64:latest ghoben/hello-world-linux-arm:latest
    denied: requested access to the resource is denied
    unauthorized: authentication required

Join The Discussion

Your email address will not be published. Required fields are marked *