Files
archy/docs/raspberry-pi-test-install.md
T

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 archipelago
  • curl -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_64 binary in its Dockerfile — exclude it.
  • App catalog unaudited for arm64 — treat individual app install failures as expected until audited (M2 doc, "ARM64 application compatibility").
  • -aarch64 tag 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)

  1. Arch-aware image selection in the orchestrator (replaces the tag suffix).
  2. A real Pi image recipe modeled on the embassyOS reference dump — Pi firmware boot (config.txt), no GRUB — replacing install-to-disk.sh's amd64 assumptions.
  3. Catalog audit for arm64, starting with the pinned bitcoin stack.