• Download 2020-08-20-raspios-buster-armhf-lite.zip from the official site
  • Install required tools
sudo pacman -S unzip util-linux docker
  • Start Docker service
sudo systemctl start docker.service

Add yourself into the docker group, otherwise permissions are needed

sudo usermod -aG docker $(whoami)
su - $(whoami)

QEMU setup

yay -S binfmt-qemu-static qemu-user-static

Verify emulation setup

grep enabled /proc/sys/fs/binfmt_misc/qemu-arm
# enabled

Alternatively, a temporary solution for most distributions:

docker run --rm --privileged docker/binfmt:820fdd95a9972a5308930a2bdfb8573dd4447ad3

Run privileged containers with caution, at least peek into the container's layers before running

yay -s dive
dive docker/binfmt:820fdd95a9972a5308930a2bdfb8573dd4447ad3

The tool displays some details about the files it is concerned with, a narrow layout

─────── ┃ ● Current Layer Contents ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Layer   Permission     UID:GID       Size  Filetree
   0    drwxr-xr-x         0:0     1.1 kB  ├── etc
   1    drwxr-xr-x         0:0     1.1 kB  │   └── binfmt.d
   2    -rw-rw-r--         0:0     1.1 kB  │       └── 00_linuxkit.conf
        drwxr-xr-x         0:0      17 MB  └── usr
        drwxr-xr-x         0:0      17 MB      └── bin
        -rwxr-xr-x         0:0     2.2 MB          ├── binfmt
        -rwxr-xr-x         0:0     4.1 MB          ├── qemu-aarch64
        -rwxr-xr-x         0:0     3.6 MB          ├── qemu-arm
        -rwxr-xr-x         0:0     3.9 MB          ├── qemu-ppc64le
        -rwxr-xr-x         0:0     3.2 MB          └── qemu-s390x

Mount

  • Extract the downloaded image from the archive
unzip <raspios-image>.zip
  • Associate the image with a loop device
sudo losetup --read-only --show -fP <raspios-image>.img
# /dev/loop0

Inspect image partitions if needed

lsblk -o name,label /dev/loop0

Look for the rootfs label

NAME      LABEL
loop0
├─loop0p1 boot
└─loop0p2 rootfs
  • Mount the root filesystem partition, if not done automatically by your distribution
sudo mkdir /tmp/raspios
sudo mount -o ro /dev/loop0p2 /tmp/raspios

Import

  • Create a Docker image from Raspios root filesystem
sudo tar c -C /tmp/raspios . | docker image import - raspios-lite-armhf:buster
  • Create and run a Docker container from the image
docker run -it --name raspios_bare raspios-lite-armhf:buster /bin/bash

Enter the container again in case of an accidental exit

docker start -ai raspios_bare
  • Clean
sudo umount /tmp/raspios
sudo losetup -d /dev/loop0

Container image manipulation

wget -qO- https://deb.nodesource.com/setup_14.x | bash -
apt install nodejs
npm i -g pkg
  • Fetch a pre-built binary of Node for armhf from a repository
wget https://github.com/yao-pkg/pkg-binaries/releases/download/v1.0.0/fetched-v14.4.0-linux-armv6 -P /root/.pkg-cache/v2.6/
  • Exit the container by pressing Ctrl-D or typing the exit command
  • Commit the changes to the image for a reuse
docker commit raspios_bare raspios_node_pkg

You can safely remove the bare container now

docker rm raspios_bare

Packaging

  • Create a sample script for packaging
echo 'console.log("Hello World")' > index.js
  • Create a temporary container, mount a current folder as /build in it and package it for linux
docker run --rm -v $PWD:/build raspios_node_pkg pkg -t linux --out-dir /build /build/index.js
  • An armhf Node executable named index is created in a current directory
file index
# index: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (GNU/Linux), ...

Make an executable script to streamline process and run i.e. like armpkg index.js

#!/bin/bash
docker run --rm -v $PWD:/build raspios_node_pkg pkg -t linux --out-dir /build "/build/$1"

Done!