Since the release of Apple’s M1 Chips, ARM architecture has expanded to a larger user base. The power efficiency and performance of the new chips have made them quite desirable. Unfortunately, most of the existing advanced developer-required toolchains were for x86_64 architecture in many cases.

This blog post introduces a brief overview of how to set up a container runner on your ARM Macbook and how to create an AArch64 Open Container Initiative (OCI) container image from the scratch.

The example builds $\rm\TeX$ Live for $\rm\LaTeX$ since I could not find an existing up-to-date image after I sold my soul to the devil by getting M2 Apple device.

There might be (there are) simpler ways to build LaTeX documents on your Macbook, but this gives us the most control in a reproducible environment!

Selecting a container runner

There are already quite many options to run OCI containers on ARM and MacOS. The obvious choice would be Docker Desktop on Mac, but we might not want yet-another Chromium instance to consume battery. We also like open-source software.

All of the current options are QEMU based since containers are based on the Linux kernel:

  • Colima as a minimal CLI-based daemon (containerd/dockerd) with Lima
  • Finch is AWS backed and aims to bring a native client on MacOS for nerdctl and containerd with Lima. Includes core features and there is development to do.
  • Podman (No daemon, based on libpod)
  • Podman Desktop (GUI, no daemon, based on libpod)
  • Rancher (GUI, containerd/dockerd and Lima)

As noted, regardless of the selection, we need a VM instance to run our Linux kernel.

As daemonless and rootless option, in this blog, we use Buildah to build our images, and Podman to run them.

Podman and Buildah

Podman can be installed by using Homebrew or Nix. Podman can build OCI images from the Dockerfile. There is an advanced tool called Buildah, but unfortunately, Buildah cannot be directly used, as it is for Linux only. Some of the features will work on MacOS (if installed with Nix), but not all of them, and images will be separated from the Podman VM.

Podman build command uses a subset of Buildah and it is sufficient for our needs for now; podman build is equivalent to buildah build -f Dockerfile Buildah can be used from the VM or inside another container, but that is too complicated for this post.

Installing Podman on Mac

We will use Nix to install QEMU and Podman.

nix-env -iA nixpkgs.qemu nixpkgs.podman 

Before anything works, we need to set up a virtual machine. At this point, it is important to note that you cannot mount folders into the containers unless you mount them into the VM too. So double mount is required; mount the folder into the VM, and mount the folder from VM into the container.

You could either mount a specific limited directory into the machine or the whole home directory, for example. Mounting the whole home directory might not be advised, but I will do it for demonstration purposes. CPU count and memory size has been also increased.

podman machine init -m 4096 --cpus 2 -v "$HOME:$HOME"
podman machine start
podman info

The final command should showcase that we are indeed running CoreOS on ARM instructions inside a VM:

host:
  arch: arm64
  buildahVersion: 1.28.0
  cgroupControllers:
  - cpu
  - io
  - memory
  - pids
.
.
  distribution:
    distribution: fedora
    variant: coreos
    version: "37"
.
.
.

Creating Arch Linux image from scratch

Arch Linux has an excellent wiki and collection of up-to-date packages. For that reason, we install LaTeX on that distribution. However, there is no official container image for ARM-based Arch Linux.

We can create a base image from the scratch; we download the official AArch64 multiplatform release, and since Linux is purely a file-based system, we just import the contents into the container, and it should work.

curl -L http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz | podman import - archlinuxarm

This will create a new image with the namespace localhost/archlinuxarm:latest.

You can try it out

podman run --rm -it archlinuxarm bash

$\rm\TeX$ Live image

The constant change of indexes is a problem with the use of Arch Linux in containers. If the container base is not updated for a while, there will be conflicts. The release of packages requires PGP signatures, and new authors will bring new keys. Sometimes keys change or deprecate, too. The following image takes this into account and should be able to update its keyring automatically. For security reasons, this is often recommended to do manually, but we will trust Arch Linux maintainers and supply chain for now.

Since we are going to build documents, the correct time might be important. We will make it possible to guarantee the correct timezone.

We might also want to install additional CTAN packages, which are not included in the Arch Linux repository. For that, we need The TeXLive Package Manager (tlmgr), which is often challenging to install. The following example installs EB Garamond font and setspace package.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
FROM archlinuxarm:latest

# If you want to use custom mirror to download packages instead of the automatic geolocation
# COPY mirrorlist /etc/pacman.d/mirrorlist

# Define timezone for container
ENV TZ=Europe/Helsinki

RUN ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime && \
    echo "$TZ" > /etc/timezone && \
    # If you are using BuildKIt instead of Buildah, see https://github.com/moby/buildkit/issues/1267 \
    # sed -i -e 's~#IgnorePkg.*~IgnorePkg = filesystem~g' '/etc/pacman.conf' && \
    # /etc/hosts and /etc/resolv.conf are read-only, filesystem package attempts to modify them
    pacman-key --init && pacman-key --populate archlinuxarm && \
    pacman-key --refresh-keys && \
    yes | pacman -Sy --noconfirm archlinux-keyring && \
    yes | pacman -Syyu --noconfirm && \
    pacman -S --noconfirm texlive-most \
    zsh \
    grml-zsh-config \
    wget \
    vim \
    # Reduce image size \
    && pacman -Sc --noconfirm && \
    rm -rf /var/cache/pacman/pkg/ \
    && usermod --shell /bin/zsh alarm


ENV TLMGR /usr/share/texmf-dist/scripts/texlive/tlmgr.pl
# For some reason, default tlmgr.pl contex filepath is in wrong location in Arch Linux 
RUN sed -i '/\$Master = "\$Master\/\.\.\/\.\.";/c     \$Master = "\${Master}\/\.\.\/\.\.\/\.\.";' $TLMGR

USER alarm
WORKDIR /home/alarm

RUN echo "alias tlmgr='${TLMGR} --usermode'" >> /home/alarm/.zshrc && \
    ${TLMGR} --usermode -v init-usertree && ${TLMGR} --usermode install ebgaramond setspace

ENTRYPOINT ["/bin/zsh"]

Generated image will have the latest TeX Live distribution with a working package manager and desired default timezone. On runtime, the timezone can also be changed to -e TZ=Europe/Amsterdam with Podman parameter.

IEEE Conference template as an example

As an example, we will build IEEE Conference template. The template is provided in here.

Download and extract it:

curl -o ieee_template.zip -L https://www.ieee.org/content/dam/ieee-org/ieee/web/org/pubs/conference-latex-template_10-17-19.zip && 7z x ieee_template.zip

Mount the source into our container. Note the --userns option; it maps the ownership of the volume inside the container to the correct non-root user.

cd Conference-LaTeX-template_10-17-19 
podman run --userns=keep-id:uid=1000,gid=1000 --rm -itv "$(pwd):/latex" texlive:latest

To build our document with LuaLatex and to also detect new changes

cd /latex
latexmk -pdflatex=lualatex  -pdf -pvc conference_101719.tex

And the PDF will finally appear in the mounted folder and will be rebuilt if the source is changed.

Conclusion

Using Podman to build PDF files might be overly complicated, but it gives us a working and reproducible environment. LaTeX can be messy sometimes, and it is good to have a chance to roll back to the previous working environment version, which can be quickly removed as well.