Skip to main content

ARM64 Docker Images

ARM64 is a fascinating contender in the world of computing. It is incredibly prevelant in mobile devices like smartphones, probably most notably due to the efficiency, but also heat generation and power usage constraints. ARM64 had never really been anything to even talk about when it came to computing or cloud providers until recently.

The giants like AWS with Graviton2, and Azure, Google, and Oracle with Ampere Altra have all made a push towards ARM64 cloud compute, with significant cost savings and performance improvements for some workloads.

Sounds great and all (and it is), but really only when you're rolling your own software and you own every step of the CI and CD process. Building for ARM64 is easy with Docker, all you need is an ARM64 machine, or QEMU to emulate a different architecture.

Why?

Oracle Cloud Infrastructure and a Raspberry Pi 4 Model B really pushed me to explore how I can shift my services and self-hosted software over to ARM64. Considering Oracle give away a 4 vCPU 24GB RAM Ampere Altra compute instance for free. The problem is that not all software I want to run is built for ARM64, and this introduced a few challenges.

How?

Docker images were an obvious first step, the ability to build a single image tag for multiple architectures using Buildx was very appealing (initially I was also experimenting with multi-architecture Kubernetes clusters - which is an entirely different set of challenges). And I went through a few different iterations of my build process before settling on the standard below:

  1. Github Actions
  2. Github hosted runners
  3. Docker buildx
  4. Appending an Ampere Altra Oracle compute instance with 1 vCPU and 8GB RAM to the Docker buildx context using SSH

Doing this I am able to natively build AMD64 and ARM64 images at a reasonable performance level.

info

In the past I have successfully used QEMU to build my own software for AMD64 and ARM64. We have also had success with this in production at my workplace. But I did run into a couple of issues when using QEMU to emulate ARM64 for multi-architecture builds in one instance and I wasn't able to work around this.

Additionally, emulating ARM64 using QEMU is significantly slower than native. So don't be alarmed at longer build times if you go down this route.

To get started building multi-architecture images, the Docker buildx manual is really all that's needed.

tip

My Github repo with all the code to build the images can be found here maxtroughear/docker-images

ClamAV

ClamAV isn't the first image I have rebuilt for ARM64, but it was one of the easiest and is what I would consider a standardised template to use for most other image builds.

Building ClamAV as a docker image was a prerequisite for building mailcow-dockerized, which is detailed below.

build-clamav.yaml

mailcow-dockerized

warning

mailcow-dockizered now natively supports ARM64. Please use the natively supported versions instead (starting from 2024-01)

mailcow-dockizered is a collection of images that have been linked together using a docker network to create a relatively easy to manage all-in-one mail server.

Due to the way that the mailcow images are built, it is not possible to simply copy paste the afore mentioned ClamAV build workflow for the mailcow-dockerized images. There are a number of issues that must be addressed.

Deploying

Updating

cd /opt/mailcow-dockerized
sudo ./update.sh --skip-start
wget -q --output-document - https://github.com/maxtroughear/docker-images/raw/main/mailcow-dockerized-arm64/patch.sh | sudo bash
sudo docker compose pull
sudo docker compose up -d --remove-orphans --no-build
sudo docker image prune

Building multiple images

Due to the number of images that need to be built, it is possible to use docker-compose to define the build files and image repos and tags. I've overritten the image repos and tags in my own docker-compose.override.yml so that the images are stored in my Github Container Repository with tags that are still inline with the original mailcow-dockerized tags.

It would be annoying to have to write out each individual docker buildx build command for the number of images that mailcow has. docker compose build works to build every image of a docker-compose.yml file, but it doesn't support buildx and multi-architecture builds. Lucky for us we have docker buildx bake which allows for parallelised multi-architecture builds from a range of different sources:

  • docker-compose.yml
  • docker-compose.yaml
  • docker-bake.json
  • docker-bake.override.json
  • docker-bake.hcl
  • docker-bake.override.hcl

build-mailcow.yaml

Making Dockerfiles compatible with ARM64

Some images clamd, dovecot, rspamd, and sogo aren't compatible with ARM64 out-of-the-box and require some modification.

clamd

For the original mailcow-dockerized clamd image, the official clamav/clamav image is used as a base. The official image does not yet support ARM64 so the previously mentioned ClamAV image is used instead.

Dockerfile

dovecot

The original mailcow-dockerized dovecot image installs debian packages from the official dovecot apt repo. This repo only contains binaries for AMD64 so it cannot be used. In order to get up-to-date versions of dovecot, I resorted to using debian:bookworm-slim as a base image as it has recent versions of dovecot available in it's built-in repos. In the future I would like to avoid using a testing version of Debian as the base and instead build dovecot from source so that I can better control the version and packages used.

Dockerfile

rspamd

Much the same as dovecot the rspamd official apt repo only contains AMD64 binaries so it cannot be used out-of-the-box. In this case I opted for using alpine:3.16 as the base image and installing rspamd from the alpine repos. In the future I would like to avoid using an entirely different operating system as the base image and instead build rspamd from source for the same reasons as dovecot.

Dockerfile

docker-entrypoint.sh

sogo

caution

Work-in-progress