Trying to understand Docker multiplatform builds and emulation

Hello,

To start off, I’m using an M1 macbook pro with the docker preview version. My understanding is that this means the Docker VM has the arm64 architecture. I’m trying to understand how Docker can build multiplatform images and run them, possibly natively or with emulation. I feel like I’m pretty close to understanding how this works, but after applying this to my own images I’m left somewhat confused.

To give you an idea of what I’m trying to accomplish, I’ll explain it shortly. I have a project on GitHub that has a deployment action which will build a new image every time I release a new version. The build is based on the hayd/deno container. As far as I can tell, this container only has linux/amd64 versions (I might be wrong, but I don’t see any linux/arm64 tags). It will then push this image to a private registry and pull + restart the container on an amd64 server. This all works perfectly fine.

Now, since receiving my new arm64 macbook, I’ve been interested in running this image here. I was expecting that I’d have to emulate the image since the base image only supports amd64. I changed my workflow by adding the arm64 platform, this was really easy using the docker/build-push-action for GitHub actions. I ran the workflow, pushed it to the registry and pulled it on my arm64 macbook, ran the image and… it worked? Inspecting the image revealed that I pulled a linux/arm64 image. I also pulled the image on my amd64 server, and yes, it pulled the linux/amd64 version.

Of course, this is all great, but I’m trying to understand what happens “behind the scenes”. How is it possible that Docker takes an amd64-only image and “magically” converts it to an arm64 image? Or is it secretly emulating?

I’m also trying to understand how the docker action creates the manifest for the different platform versions. I’ve followed this interesting article to test buildx locally, so I’m guessing the action uses something like it. What surprises me is that I had to manually build 2 versions and create 2 tags for the arm64 and amd64 platforms, push them, then create a manifest which points to these tags and I was finally able to pull the correct versions. This creates 2 extra tags though, which the github action does not. This also really confused me.