Docker images for multiple platforms
10 September 2023 | 7:02 pm

Ever since I got an M2 Mac, I noticed more and more warnings around the images I wanted to use to not be native to my platform. Well, I was aware that Docker images and their content were platform/architecture-specific, but I’ve never really “felt” the effects of that before except when I played around with Docker on Raspberry PIs 🤦‍♂️ So now that I have both an M2 Mac and an old Intel-based one I started to look into options for how to bundle my applications up for both target systems.

As far as I can tell, there are two options:

  1. Create a separate image for every target like myapp:latest-amd64 and myapp:latest-arm64
  2. Have a single image (or at least make it look like a single image) that includes all the architectures that you want

The first approach is not all that user-friendly as users now have to be aware of what platform they are on. It’s just easier for the user to just pull myapp:latest and let the Docker runtime handle the rest.

I was curious how some of the tools I regularly use solved this and so I took a look at goreleaser. If you look at the goreleaser/goreleaser:v1.20.0 entry on Docker Hub, you’ll notice that it supports two platforms: linux/amd64 and linux/arm64. Both of these have image digests listed so there seems to be a completely standalone image also available for each of them:

  • goreleaser/goreleaser:v1.20.0-arm64
  • goreleaser/goreleaser:v1.20.0-amd64

These two images are then somehow bundled into one entry for easier consumption. To find out more, I used skopeo inspect --raw docker://goreleaser/goreleaser:v1.20.0

   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
   "manifests": [
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 2632,
         "digest": "sha256:b2c31539d194880b293399c7ac98bc032cb9bbb098c512b0abaa55c8c1541d34",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 2632,
         "digest": "sha256:8bbc939928d0c06c05208004c0f013957ff4d7669f25b5b850f63524f8c67e99",
         "platform": {
            "architecture": "arm64",
            "os": "linux"

This entry is actually a list of so-called manifests (one for each of the platforms). The runtime will then pick the right manifest for the platform you’re on (linux/arm64 for M1/2 Macs, linux/amd64 for Intel in my case).

How to create a multi-arch image?

So, how do you now create such a multi-platform image? The Docker manual has a detailed guide with multiple options but I wanted to create a minimal example using this Dockerfile:

FROM alpine:3.18
RUN echo "Building on $BUILDPLATFORM for $TARGETPLATFORM" > /log

In this example, I want to have an image on DockerHub with the name zerok/multiarch-test:latest that I can use on linux/amd64 and linux/arm64.

To make platform specific images, I’ll use buildx:

docker buildx build --platform linux/amd64 \
    --tag zerok/multiarch-test:latest-amd64 \
    --output=type=docker,dest=latest-amd64.tar .

docker buildx build --platform linux/arm64 \
    --tag zerok/multiarch-test:latest-arm64 \
    --output=type=docker,dest=latest-arm64.tar .

This will write the generated images to separate .tar files. For generating the manifest list I’ll need to have these also available within the “normal” Docker engine:

docker load --input latest-arm64.tar
docker load --input latest-amd64.tar

Finally, with those images in place, I’ll create a single manifest file and push that to DockerHub:

docker manifest create \
    --insecure zerok/multiarch-test:latest \
    --amend zerok/multiarch-test:latest-amd64 \
    --amend zerok/multiarch-test:latest-arm64

docker manifest push zerok/multiarch-test:latest

Actually, all of these steps can also be combined into a single command which is is handy if you don’t need to process the images before pushing them to the registry:

docker buildx build \
    --platform linux/amd64 \
    --platform linux/arm64 \
    --tag zerok/multiarch-test:latest \
    --push .

Some resources

Thanks to this little experiment/investigation I found a lot of useful resources around manifests and related topics:

Vegetarian dining at Speis am Lendhafen
3 September 2023 | 6:28 pm

Since we had something to celebrate last week, we wanted to also celebrate it with a fancy dinner. During the last couple of years, a few really nice vegetarian/vegan restaurants have opened in Graz and so we decided to try one that offered a bit of extra fanciness: “Die Speis am Lendhafen”, a tiny but very modern restaurant located at Mariahilferplatz. It only has about five indoor tables and a couple on the outside. Since it was raining on that day, the place was unfortunately pretty empty with us being the only guests in the beginning.

Unlike a lot of other places in this price category, they offer a 5- or 7-course menu which sounded exactly what I wanted for such a celebration! When ordering you also get to choose between a vegan or vegetarian option. The main difference is that two of the course also contain some cheese if you take the vegetarian route. Since I love cheese, I went that route!

Grilled zuccini with goat cheese

And I wasn’t disappointed! You can find the whole menu here and I have to say, each course was delicious! My personal highlight was the greeting from the kitchen (second course) which was a grilled piece of zucchini with pumpkin seed oil, goat cheese, and roasted nuts/seeds! I just love those little surprise courses from the kitchen 😍

To summarise: I can absolutely recommend “Die Speis am Lendhafen” if you want to have a fancy vegetarian dinner! They also have a lot of other meals on their menu so I’m pretty sure there should be something for you unless you absolutely want to at some meat 😅

Requiring a pipeline run for merging a pull-request
30 August 2023 | 7:50 pm

I want to configure my project on GitHub to require a whole pipeline run to pass before it can be merged. This is supported through “Branch protection rule” where you have to manipulate two settings:

  • “Require a pull request before merging”
  • “Require status checks to pass before merging”

While the first is quite straight forward, the second requires a bit of explanation. If you enable this flag then you have to specify one or more “statuses” that should be successful before the PR can be merged. A status is more or less just an indicator that you can attach to a commit through the GitHub API.

But does this mean that I have to write a custom action in my pipeline that manipulates such a status? No! Every job name in your pipelines becomes its own status. So if I now have a workflow with the name “CI” and a job with the name “main”, then I can pick “main” from the select box there and make it mandatory.

This has the side-effect that I should probably rethink how I name the jobs in my workflows…

More News from this Feed See Full Web Site