8.9 KiB
Raspberry Pi ARM64 Test Install
Status: Phase 0 — tooling + runbook. Validates the ARM64 port on commodity
hardware (Pi 5) before committing to a real Pi image recipe. This is not
a shippable ARM image: the ARM image is explicitly out of scope for phase 1
(see kdump-rasdaemon-design.md), and the porting gap analysis in
apple-silicon-m2-development-research.md (written for M2/Asahi) still has
open items — most notably arch-aware OTA. The Pi is easier than that doc's
M2 plan in two ways: stock Debian 13 trixie (no Asahi 16K-page concerns) and
Xorg/Mesa works, so no Wayland kiosk port is needed for a headless test.
Reference material for the eventual image recipe: image-recipe/raspberrypi/
holds a dissected Start9 embassyOS Pi image (config.txt/cmdline.txt boot
chain, embassyOS partition layout, init_resize.sh). Nothing consumes it yet.
Dedicated ARM64 build machine: the M2 Air (the
apple-silicon-m2-development-research.md reference machine). In its build
role it needs no Asahi — macOS + Docker / podman machine run arm64 Linux
containers natively, so every build below runs at full speed with no qemu.
Asahi only matters for the M2 as a target device, which remains a separate
milestone in that doc. Keep x86_64 builds on the x86_64 fleet hosts —
emulating amd64 on Apple Silicon is the slow direction.
What changed to enable this
| Change | File |
|---|---|
Optional arch arg (x86_64 default, aarch64); arch-suffixed tags; SHA-256 + GPG fail-closed verification unchanged |
scripts/build-bitcoin-image.sh |
ARCH=aarch64 cross build: cargo-zigbuild on host (fast) or emulated rust:trixie Docker (slow, zero setup). Backend is rustls-only, so no sysroot libs needed |
image-recipe/scripts/build-backend.sh |
| Dockerfile comments mention the aarch64 tarballs | apps/bitcoin-core/Dockerfile, apps/bitcoin-knots/Dockerfile |
| Frontend | unchanged — Node build, arch-agnostic |
Default (no-arg) behavior of both scripts is byte-identical to before.
Hardware
- Raspberry Pi 5 — 4 GB is fine for signet; 8 GB for anything mainnet-ish
- 27 W PSU; storage: NVMe HAT recommended for a node, SD works for a smoke test
- Ethernet
Part 1 — Build on the M2 (native ARM64)
Requirements on the M2: Docker (backend script) and podman + podman machine
(bitcoin image script). No binfmt registration — arm64 runs natively.
Backend:
ARCH=aarch64 image-recipe/scripts/build-backend.sh
# → image-recipe/build/backend/archipelago-aarch64 (native-speed arm64 build)
Bitcoin Core image — the app manifest pins
source.archipelago-foundation.org/lfg2025/bitcoin:28.4 (apps/bitcoin-core/manifest.yml),
so build that version for arm64. On the M2 the smoke test also runs natively,
so the full fail-closed pipeline (SHA-256 + GPG + bitcoind --version)
executes at full fidelity:
scripts/build-bitcoin-image.sh core 28.4 aarch64 # builds, verifies, pushes
# or build + verify only, then transfer the image as a tarball:
NO_PUSH=1 scripts/build-bitcoin-image.sh core 28.4 aarch64
podman save -o bitcoin-28.4-aarch64.tar \
source.archipelago-foundation.org/lfg2025/bitcoin:28.4-aarch64
If the M2 has registry credentials, just push and let the Pi pull directly (skip the save/load hop).
Frontend (arch-agnostic — build on either machine):
image-recipe/scripts/build-frontend.sh
# → image-recipe/build/frontend/ (static dist)
Transfer to the Pi: archipelago-aarch64, bitcoin-28.4-aarch64.tar,
frontend dist tarball, plus image-recipe/configs/archipelago.service and
nginx-archipelago.conf from the repo.
Alternative — build on the x86_64 host (ThinkPad)
Backend cross-compiles natively with cargo-zigbuild (no emulation):
sudo pacman -S cargo-zigbuild # extra repo; pulls zig
ARCH=aarch64 image-recipe/scripts/build-backend.sh
The bitcoin image build runs its RUN steps under qemu — register handlers once; the smoke test there is best-effort (SHA-256 + GPG remain the real gate, re-verify natively on the Pi):
podman run --privileged quay.io/podman/qemu-user-static --reset -p yes
NO_PUSH=1 scripts/build-bitcoin-image.sh core 28.4 aarch64
Part 2 — Pi setup
Flash Raspberry Pi OS Lite (64-bit) — it's Debian 13 trixie, same base as
the fleet images — headless with SSH enabled. For NVMe boot set
BOOT_ORDER in the bootloader EEPROM (rpiboot/rpi-eeprom-config) and, if
needed, dtparam=pciex1 in /boot/firmware/config.txt.
Then:
# rootless podman
sudo apt update && sudo apt install -y podman uidmap
image-recipe/scripts/install-podman.sh
# service user — uid 1000 must match XDG_RUNTIME_DIR=/run/user/1000 in the unit
sudo useradd -m -u 1000 -s /bin/bash archipelago
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 archipelago
# backend
sudo install -m755 archipelago-aarch64 /usr/local/bin/archipelago
sudo cp archipelago.service /etc/systemd/system/archipelago.service
# The unit's ExecStartPre lines use -/+ prefixes, so missing helpers
# (ota-crash-guard.sh etc.) are tolerated; trim them if they aren't.
sudo systemctl daemon-reload && sudo systemctl enable --now archipelago
# Gaps found by the arm64 systemd-container test (2026-10-11) — the unit and
# nginx config need these or they fail to start:
sudo groupadd -f debian-tor && sudo groupadd -f fips
sudo usermod -aG dialout,debian-tor,fips archipelago
sudo mkdir -p /home/archipelago/.local/share/containers /home/archipelago/.config/containers \
/var/lib/containers /etc/containers /opt/archipelago/scripts /var/lib/archipelago
sudo chown -R archipelago:archipelago /home/archipelago /var/lib/archipelago
# nginx config includes snippets and a TLS cert (self-signed is fine for a test)
sudo cp -r image-recipe/configs/snippets /etc/nginx/
sudo mkdir -p /etc/archipelago/ssl && sudo openssl req -x509 -newkey rsa:2048 -nodes -days 30 \
-subj /CN=archy-test -keyout /etc/archipelago/ssl/archipelago.key -out /etc/archipelago/ssl/archipelago.crt
sudo cp /etc/archipelago/ssl/archipelago.crt /etc/archipelago/ssl/ca-download.crt
# frontend + nginx (serves static, proxies API to 127.0.0.1:5678)
sudo mkdir -p /opt/archipelago/web-ui && sudo rsync -a dist/ /opt/archipelago/web-ui/
sudo apt install -y nginx
sudo rm -f /etc/nginx/sites-enabled/default
sudo cp nginx-archipelago.conf /etc/nginx/conf.d/archipelago.conf
sudo systemctl reload nginx
# bitcoin image: load, then retag to the pinned manifest ref (see caveat below)
sudo -u archipelago podman load -i bitcoin-28.4-aarch64.tar
sudo -u archipelago podman tag \
source.archipelago-foundation.org/lfg2025/bitcoin:28.4-aarch64 \
source.archipelago-foundation.org/lfg2025/bitcoin:28.4
Registry caveat: manifests pin exact image refs and the orchestrator has
no arch field, so retagging the loaded image to the pinned ref satisfies the
install without a registry round-trip only if the installer doesn't
force-pull. If it does, either push the -aarch64 image under the pinned
name to a registry you control, or run bitcoind manually for the test and
skip the UI install path.
For a quick sanity check independent of the app installer:
sudo -u archipelago podman run --rm \
source.archipelago-foundation.org/lfg2025/bitcoin:28.4 bitcoind --version
Part 3 — Validation checklist
uname -m→aarch64; backend up:systemctl status archipelagocurl -s http://127.0.0.1/health(via nginx) responds- The retagged 28.4 image runs natively (command above) — proves no 4K-page/alignment surprises (Pi is 4K, so this should be a non-issue — it's the Asahi targets that need that audit)
- Dashboard reachable from the LAN at
http://<pi-ip>/ - Bitcoin app installs from the UI and the container starts; for a cheap test run signet or pruned rather than a full mainnet sync
- Memory headroom: bitcoind + backend RSS vs total RAM (Pi 5 4 GB is the floor; note it for the eventual image spec)
Known ARM gaps — expected failures, not test blockers
- OTA has no architecture field (
core/archipelago/src/update.rs) — never OTA a Pi test node; it would try to replace the backend with x86 binaries. - barkd downloads a
linux-x86_64binary in its Dockerfile — exclude it. - App catalog unaudited for arm64 — treat individual app install failures as expected until audited (M2 doc, "ARM64 application compatibility").
-aarch64tag suffix is a stopgap — real fix is arch-aware image selection + manifest lists; see the M2 doc's milestones.- kdump/rasdaemon phase 1 is amd64-only by design
(
kdump-rasdaemon-design.md).
Where this goes next (if the test passes)
- Arch-aware image selection in the orchestrator (replaces the tag suffix).
- A real Pi image recipe modeled on the embassyOS reference dump — Pi
firmware boot (
config.txt), no GRUB — replacinginstall-to-disk.sh's amd64 assumptions. - Catalog audit for arm64, starting with the pinned bitcoin stack.