---
title: "Run an ARM32 Docker daemon on ARM64 servers"
date: 2020-09-24T10:30:00+02:00
authors:
  - robert-kaussow
tags:
  - Automation
  - Container
  - Sysadmin
resources:
  - name: feature
    src: "images/feature.jpg"
    params:
      anchor: Center
      credits: >
        [Yannick Pipke](https://unsplash.com/@joker2000) on
        [Unsplash](https://unsplash.com/s/photos/cpu)
---

In the last days I worked on a suitable setup for a [Drone CI](https://drone.io/) server to support multi-arch builds. While the setup for common x86 Drone runners is easy, working with setups for ARM, especially ARM32, is a bit tricky. The easiest way would be to have native servers of the respective architecture available. However, it's difficult to find hosting offers for ARM at all - for ARM32 this seems almost impossible. I decided to use Amazon EC2 ARM64 servers, they are relatively cheap and can also be used as a private customer.

<!--more-->

But how do you turn an ARM64 server into an ARM32 server? Basic requirement is an ARMv8 CPU. This type supports (in most cases) both `AArch32` and `AArch64`. The first step is to enable multi-arch support on your operating system. I use Ubuntu 18.10 for this setup:

```Shell
dpkg --add-architecture armhf
```

Then add the Docker CE repository for ARM32 and install the packages:

<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- spellchecker-disable -->
{{< highlight bash "linenos=table" >}}
# add GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# add repos
add-apt-repository \
   "deb [arch=armhf] https://download.docker.com/linux/ubuntu \
   $RELEASE \
   stable

# install
apt-get update
apt-get install docker-ce:armhf
{{< /highlight >}}
<!-- spellchecker-enable -->
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

To use the right architecture within Docker containers you still have to force the Docker daemon into ARM32 mode. This can be accomplished by two systemd overwrites:

<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- spellchecker-disable -->
{{< highlight bash "linenos=table" >}}
# overwrite docker.service
# /etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/setarch linux32 -B /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

# overwrite containerd.service
# /etc/systemd/system/containerd.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/setarch linux32 -B /usr/bin/containerd
{{< /highlight >}}
<!-- spellchecker-enable -->
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

That's it. Don't forget to reload and restart the systemd daemon:

```Shell
systemctl, daemon reload
systemctl, restart, docker
```

What you get is a Docker daemon that runs in ARM32 mode. This can be used for example to start a Golang container and build apps without cross-compile.

<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- spellchecker-disable -->
{{< highlight bash "linenos=table" >}}
root@ip-10-0-225-151:~# docker version
Client: Docker Engine - Community
 Version:           19.03.13
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        4484c46
 Built:             Wed Sep 16 17:07:23 2020
 OS/Arch:           linux/arm
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.13
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       4484c46
  Built:            Wed Sep 16 17:01:08 2020
  OS/Arch:          linux/arm
  Experimental:     false
 containerd:
  Version:          1.3.7
  GitCommit:        8fba4e9a7d01810a393d5d25a3621dc101981175
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
root@ip-10-0-225-151:~# docker run alpine uname -a
Linux dbad5ddeb5ea 5.3.0-1035-aws #37-Ubuntu SMP Sun Sep 6 01:17:41 UTC 2020 armv8l Linux
{{< /highlight >}}
<!-- spellchecker-enable -->
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

Just a word of warning. I'm not a hardware specialist and there might be some situations where this setup does not work. Furthermore it seems that some operating systems have problems to recognize `armv8l` as ARM32 architecture. For OpenSuse as an example I had to force `zypper` into `armv7hl` mode to get it working. After this step building ARM32 OpenSuse Docker images also works for me.

```Shell
sed -i 's/# arch = s390/arch = armv7hl/g' /etc/zypp/zypp.conf
```

If you want to give it a try, I've prepared a minimal Cloud-Init configuration:

<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- spellchecker-disable -->
{{< highlight yaml "linenos=table" >}}
#cloud-config

apt_reboot_if_required: false
package_update: true
package_upgrade: true

bootcmd:
  - [ dpkg, --add-architecture, armhf  ]

apt:
  sources:
    docker.list:
      source: deb [arch=armhf] https://download.docker.com/linux/ubuntu $RELEASE stable
      keyid: 0EBFCD88

packages:
  - 'docker-ce:armhf'

write_files:
  - path: /etc/systemd/system/docker.service.d/override.conf
    content: |
      [Service]
      ExecStart=
      ExecStart=/usr/bin/setarch linux32 -B /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

  - path: /etc/systemd/system/containerd.service.d/override.conf
    content: |
      [Service]
      ExecStart=
      ExecStart=/usr/bin/setarch linux32 -B /usr/bin/containerd

runcmd:
  - [ systemctl, daemon-reload ]
  - [ systemctl, restart, docker ]

{{< /highlight >}}
<!-- spellchecker-enable -->
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
