12 KiB
Plan: aarch64 (Generic UEFI ARM64) build for omarchy-iso
Context
omarchy-iso currently produces a single x86_64 Arch Linux live ISO. The build is x86-baked from top to bottom — profile arch, package list filename, microcode, kernel choice (linux-t2), bootloader configs (BIOS syslinux + UEFI GRUB), squashfs BCJ filter, Node.js download URL, QEMU smoke-test, release filename, and CI runner.
Target: a parallel generic UEFI aarch64 ISO — boots on Ampere servers, AWS Graviton VMs, Snapdragon X laptops, ARM dev kits. Anything that exposes vanilla UEFI + ACPI. Apple Silicon (Asahi) and SBCs (U-Boot/rpi-firmware) are out of scope.
Assumption: cross-repo dependencies are someone else's problem to land first. This plan only covers omarchy-iso. The hard prerequisites are listed up front so it's clear what blocks the first green build.
Hard prerequisites (NOT in this plan, but block any green build)
These exist outside omarchy-iso. Flagged for visibility — without them, nothing here boots:
- aarch64 base packages must exist somewhere we can pacman from. Vanilla Arch (
geo.mirror.pkgbuild.com) is x86_64-only — there is nocore/os/aarch64. The realistic source is Arch Linux ARM (mirror.archlinuxarm.org) forcore/extra/alarm/aur. Decision required: target Arch Linux ARM as the aarch64 base distribution. pkgs.omarchy.org/{stable,edge}/aarch64/must serve a real repo. Probed today, both return 404. omarchy-pkgs already has multi-arch build support per its README, so this is a publish step, not a port.- Omarchy installer guard at
~/.local/share/omarchy/install/preflight/guard.sh:25hard-aborts onuname -m != x86_64. Must be relaxed to allow aarch64 (or scoped behind a flag). - archinstall + Limine must work end-to-end on aarch64. archinstall supports it; Limine supports aarch64 UEFI. Worth a manual verification before committing to this bootloader path on ARM.
Approach
Add an --arch aarch64 flag to bin/omarchy-iso-make. Branch on it everywhere x86_64 is currently assumed. Don't fork the profile directory — overlay arch-specific files at build time so the two architectures share one source of truth.
Output artifact naming already uses no arch suffix until release-time renaming, so two parallel builds coexist cleanly.
Concrete changes
1. Build entrypoint — bin/omarchy-iso-make
- Add
--arch x86_64|aarch64flag (defaultx86_64for backward compat). - Pass
OMARCHY_ARCHenv var into the container. - Switch the docker image / platform per arch:
- x86_64:
archlinux/archlinux:latest(unchanged) - aarch64:
archlinuxarm/archlinuxarm:latest(ormenci/archlinuxarm— pick whichever publishes a recent multi-arch image), with--platform linux/arm64. Native arm64 host preferred; QEMU emulation works but is slow.
- x86_64:
- Don't change pacman cache mount on the host: pacman keeps per-arch subdirs, so the cache is safe to share.
2. Profile — configs/profiledef.sh
Change arch and the squashfs BCJ filter at runtime. Either:
- Keep one
profiledef.shand havebuilder/build-iso.shsedthe arch line + BCJ filter (x86→arm) beforemkarchisoruns, or - Source
OMARCHY_ARCHdirectly insideprofiledef.sh(mkarchiso sources this file with bash, so env access is fine).
bootmodes also needs to drop bios.syslinux for aarch64 (BIOS boot is x86-only) — bootmodes=('uefi.grub') on ARM.
3. Build script — builder/build-iso.sh
- Line 56–57: Node.js URL grep. Branch on
OMARCHY_ARCH:x86_64→linux-x64.tar.gzaarch64→linux-arm64.tar.gz
- Line 73: package additions. Drop
linux-t2for aarch64 (T2-only x86 kernel) — use plainlinux. Write topackages.${OMARCHY_ARCH}instead of hardcodedpackages.x86_64. - Line 77: read same
packages.${OMARCHY_ARCH}for offline mirror enumeration. - archiso releng overlay (
cp -r /archiso/configs/releng/*): the upstreamrelengprofile ships onlypackages.x86_64. For aarch64, copypackages.x86_64topackages.aarch64and prune obviously x86-only entries (memtest86+,intel-ucode,amd-ucode,edk2-shellx64 binary,syslinux). Land this as a small fixup step inbuild-iso.shrather than a full fork ofreleng.
4. archinstall package list — builder/archinstall.packages
Drop microcode for aarch64 — intel-ucode and amd-ucode don't exist for ARM. Either:
- Two lists (
archinstall.packages.x86_64,archinstall.packages.aarch64), or - One list with sentinel comments and a filter step in
build-iso.sh:80that drops microcode lines whenOMARCHY_ARCH=aarch64.
The second is less duplication for one-line drift.
5. Bootloader configs
aarch64 has no BIOS — only UEFI. Drop the syslinux path entirely on ARM.
- Skip on aarch64:
configs/syslinux/*(BIOS syslinux),configs/grub/*references toshellx64.efi/shellia32.efi(lines 81–85 ofgrub.cfg). configs/efiboot/loader/loader.conf: changedefault 01-archiso-x86_64-linux.confto a per-arch entry. Either two loader.conf files (selected by build-iso.sh) or rename the entry generically (01-archiso-linux.conf) and keep one file.configs/efiboot/loader/entries/01-archiso-x86_64-linux.conf: produce an aarch64 sibling (01-archiso-aarch64-linux.conf) at build time — same template, drop microcode initrd lines, swap title. The kernel/initrd paths use%ARCH%already ingrub.cfgbut are hardcodedx86_64in this file (line 3–4).configs/grub/grub.cfgandloopback.cfg: remove or guard the x86_64 conditionals. The%ARCH%placeholder gets substituted by mkarchiso fromprofiledef.sh:arch, so the kernel/initrd paths flip automatically. Manually-writtengrub_cpu == 'x86_64'blocks (lines 34–39, 68, 81–85) should drop on aarch64 builds — easiest done with a build-time sed for the aarch64 path, or by extracting them into a separategrub-x86_64.cfg.fragmentonly included on x86 builds.
The cleanest cut: keep one grub.cfg shared, strip the x86 shell/memtest fragments at build time when OMARCHY_ARCH=aarch64. Don't fork the file.
6. mkinitcpio preset — configs/airootfs/etc/mkinitcpio.d/linux.preset
References vmlinuz-linux-t2 and initramfs-linux-t2.img (lines 5, 8). For aarch64, swap to vmlinuz-linux / initramfs-linux.img. Generate the file at build time from a template, keyed on arch.
7. Configurator — configs/airootfs/root/configurator
Lines 320–325:
if lspci -nn 2>/dev/null | grep -q "106b:180[12]"; then
kernel_choice="linux-t2"
else
kernel_choice="linux"
fi
T2 detection is harmless on aarch64 (lspci returns no match), so kernel_choice falls through to linux. No change strictly required, but clearer to wrap the lspci probe in [[ $(uname -m) == "x86_64" ]] so the intent reads correctly. Cheap.
The archinstall JSON's mirror_config.custom_servers (lines 422–424) points at mirror.omarchy.org, mirror.rackspace.com/archlinux, geo.mirror.pkgbuild.com — none of these serve aarch64. For aarch64 the list must be mirror.archlinuxarm.org and friends. Branch the JSON template on OMARCHY_ARCH (or uname -m at runtime, since this file runs on the live ISO).
8. pacman configs — configs/pacman-online-{stable,rc,edge,offline}.conf
The $arch placeholder is pacman-resolved (matches Architecture = auto), so https://stable-mirror.omarchy.org/$repo/os/$arch adapts automatically — as long as the mirror serves aarch64. The omarchy mirror is the bottleneck (prerequisite #2). The arch-mact2 repo is x86-only by definition; gate it behind arch on aarch64 builds (drop the section).
9. Smoke-test scripts — bin/omarchy-iso-boot, bin/omarchy-vm
Detect arch from the ISO filename (or accept a flag) and switch:
- x86_64:
qemu-system-x86_64,/usr/share/edk2/x64/OVMF_CODE.4m.fd+OVMF_VARS.4m.fd(unchanged). - aarch64:
qemu-system-aarch64,-machine virt -cpu max,/usr/share/edk2/aarch64/QEMU_CODE.fd+QEMU_VARS.fd(Arch package:edk2-armvirt).
Both scripts hardcode the OVMF path; small, mechanical fix.
10. Release script — bin/omarchy-iso-release:52
latest_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*x86_64-"$INSTALLER_REF".iso | head -n1)
Hardcoded x86_64 glob. Take an arch arg, or do *${OMARCHY_ARCH}-${INSTALLER_REF}.iso. The ISO filename already contains arch (set by archiso from profiledef.sh), so this just needs the glob parameterized.
11. CI — .github/workflows/nightly-build.yml
Add a matrix:
strategy:
matrix:
arch: [x86_64, aarch64]
include:
- arch: x86_64
runner: ubuntu-latest
- arch: aarch64
runner: ubuntu-24.04-arm # GitHub-hosted ARM runner, GA
Pass --arch ${{ matrix.arch }} to the build. Upload artifacts named per-arch.
If GitHub-hosted ARM runners aren't available on this org's plan, fall back to QEMU emulation on ubuntu-latest (slow, ~3-4× build time) or self-hosted.
Files to modify (summary)
Code:
bin/omarchy-iso-make— add--archflag, branch docker image/platformbin/omarchy-iso-boot— branch QEMU binary + OVMF pathbin/omarchy-vm— same as iso-bootbin/omarchy-iso-release— parameterize ISO globbuilder/build-iso.sh— main branching: Node.js URL, package list filename, releng overlay fixups, kernel packagebuilder/archinstall.packages— filter microcode on aarch64 (sentinel comments + filter step)configs/profiledef.sh— env-drivenarch+ BCJ filter + bootmodesconfigs/efiboot/loader/loader.conf— per-arch default entryconfigs/efiboot/loader/entries/01-archiso-x86_64-linux.conf— generate aarch64 sibling at build timeconfigs/grub/grub.cfg,loopback.cfg— strip x86-only fragments on aarch64configs/airootfs/etc/mkinitcpio.d/linux.preset— template by arch (drop linux-t2 on aarch64)configs/airootfs/root/configurator— branch archinstall JSON mirror list onuname -m; optionally guard lspci T2 probe.github/workflows/nightly-build.yml— matrix x86_64/aarch64
No new files unless we go the dual-list route on archinstall.packages or efiboot loader entries; preferred path is build-time templating to keep one source of truth.
Verification
Local (host is x86_64):
bin/omarchy-iso-make --arch x86_64— must produce a byte-similar ISO to today's nightly (smoke test for regressions in the refactor).bin/omarchy-iso-make --arch aarch64— succeeds (slow under QEMU binfmt; expect 30-60 min).bin/omarchy-iso-boot release/omarchy-*-aarch64-*.iso— boots to the configurator underqemu-system-aarch64 -machine virt. Walk through the picker, confirm archinstall lays down a working system, reboot into the installed system.- Live-iso shell:
pacman -Sy && pacman -Si linuxreturns an aarch64 package frommirror.archlinuxarm.org.
On real hardware (post green QEMU run):
- AWS Graviton VM (
c7g.medium, dd ISO to a volume, attach as boot) — fastest cloud check, no hardware needed. - One physical ARM64 UEFI box if available (Ampere dev kit, Snapdragon X laptop, etc.) — the long pole.
CI:
- Both matrix legs go green on a manual
workflow_dispatchbefore merging. - Nightly produces both ISOs as artifacts.
Open risks (call out before implementation)
- mkarchiso on aarch64 is less-trodden ground. It accepts
arch="aarch64", but the upstream Arch project doesn't dogfood it. Expect to file/patch around small bugs in archiso's helper scripts. Keep the archiso submodule pin tight so a regression doesn't surprise nightly. - Limine + LUKS + Btrfs + Snapper on aarch64 — every one of these works individually on ARM, but the combination is what omarchy ships. Worth one manual end-to-end pass before declaring done.
- Apple T2 / linux-t2 /
arch-mact2repo are silently dropped on aarch64; users mistakenly trying to install the aarch64 ISO on a T2 Mac will get an obviously-wrong result. The configurator could refuse to install when arch mismatch is detected, but that's outside this plan.