install Alpine Linux on apple M1

2022-03-24

udpdate: 2024-02-01

now it is possible to install alpine on apple silicon using installer:

on macos terminal run next command

curl https://arvanta.net/asahi/aai.sh | sh

and carefully reading and following instructions which are printed on screen durring process


here is short video of the first step of installation https://arvanta.net/asahi/alpine-installer-1.mp4


it is possible to resize root FS partition after installation by using different methods and tools.

here is how I did on my machine: be careful with this

sgdisk -d 5 -n 5::+20G: -c 5:root /dev/nvme0n1
partx -u /dev/nvme0n1
resize2fs /dev/nvme0n1p5
reboot

usual disclaimer: no one is responsible for any damage/loss by using this

rest of this guide is left bellow just for historical reference

First step:

first read carefully https://asahilinux.org/2022/03/asahi-linux-alpha-release/

and keep in mind that is alpha release of asahi installer

look at section UEFI environment only (m1n1 + U-Boot + ESP), i.e. for Alpine select this option when prompted for 'OS options'

this could take some time to finish, sometimes 30-60 minutes

it will reboot after this in boot in second stage installing, follow it


when all above is finished then:

simple method:

there is ready made bootable alpine image which can be used:

download link https://dev.alpinelinux.org/~mps/m1/m1-usb-alpine-install.img.xz

download it
uncompress with:
xz -d m1-usb-alpine-install.img.xz
use dd command to 'flash' it on usb disk, for example:
dd if=m1-usb-alpine-install.img of=/dev/sdX` where sdX usb disk device

Note: script which is used to create this usb disk image is at the end of this 'guide'


plug usb disk/flash in usb port om M1:

Next, when rebooted and u-boot message appear on the the screen and wait for timeout counter, press 'Enter' before it timeouts then run

run usb_boot

if it doesn't work (with the new u-boot) run

env set boot_efi_bootmgr ; run bootcmd_usb0

Also, here are some Helpful U-Boot tricks for Asahi

grub booloader prompt should appear on display and will timeout after 10 seconds and boot Alpine in text (console) mode.

pressing enter will give 'root' prompt

from it run lsblk -f and look for partition labeled as 'EFI - UEFI'

mount it under /mnt running mount LABEL="EFI - UEFI" /mnt

then cd /lib/firmware && tar xvf /mnt/vendorfw/firmware.tar

there is /root/update-vendor-firmware which can be run with sh update-vendor-firmware to install wifi firmware if ESP partition is mounted on /mnt

remove blacklist for wifi driver by invoking rm /etc/modprobe.d/blacklist-brcmfmac.conf next load wifi driver by running

modprobe brcmfmac

then run iwd (internet wifi/wireless daemon) with

/etc/init.d/iwd start

then run iwctl command to configure wifi there are guides on internet how to use iwctl and here is old one

umount /mnt umount /mnt

if all finished without error then this usb disk could be used as rescue system


installing root filesystem on nvme partition

find the Linux partition on nvme, for example by runing lsblk -f

for example:

check is filesystem created on linux partition, if not must be created with `mkfs.fstype /dev/nvme0n1pX ((X is linux partition number)

mount /dev/nvme0n1pX /mnt (X is linux partition number)
mkdir -p mnt/boot/efi
mkdir -p /mnt/etc/apk/keys
mount -t vfat /dev/nvme0n1pX /mnt/boot/efi (X is ESP partition number)

scrip bellow is in the /root/install-nvme.sh file and if previous step is done without errors could be used to install alpine on /mnt by running
sh install-nvme.sh


#!/bin/sh

# apk add blkid parted dosfstools e2fsprogs wget tar
set -eu

# before starting this script next steps must be done replacing $TMPDIR with mountpoint of the nvme linux partition
# and replacing $ROOTDEV with nvme linux partition 

mkfs.ext4 -qL root "$ROOTDEV"
mount -t ext4 "$ROOTDEV" "$TMPDIR"
mkdir -p "$TMPDIR"/boot
mkdir -p "$TMPDIR"/boot/efi "$TMPDIR"/etc/apk/keys
mount -t vfat "$BOOTDEV" "$TMPDIR"/boot/efi

if ! mountpoint -q "/mnt"; then
	echo "/mnt is not mounted"
  exit
fi

readonly TMPDIR="/mnt"
readonly APK_KEY="alpine-devel@lists.alpinelinux.org-60ac2099.rsa.pub"
readonly MIRROR="http://dl-cdn.alpinelinux.org/alpine"

wget -O update-vendor-firmware  https://dev.alpinelinux.org/~mps/m1/update-vendor-firmware

echo "Setup apk keys and repositories"
wget -qP "$TMPDIR"/etc/apk/keys https://alpinelinux.org/keys/"$APK_KEY"
printf "$MIRROR/edge/%s\n" main community testing > "$TMPDIR"/etc/apk/repositories

echo "installing base packages"
apk --allow-untrusted --root "$TMPDIR" --arch aarch64 --initdb add \
	alpine-base alpine-baselayout alpine-conf kmod openrc \
	dbus util-linux blkid chrony \
	sysfsutils ssl_client ca-certificates-bundle alpine-keys \
	ethtool e2fsprogs libudev-zero libudev-zero-helper \
	iwd linux-firmware-none efibootmgr ttf-dejavu \
	nvme-cli agetty terminus-font openresolv tar wget
apk --allow-untrusted --root "$TMPDIR" --arch aarch64 --no-script add linux-asahi
apk --allow-untrusted --root "$TMPDIR" --arch aarch64 --no-script add grub-efi grub-mkfont

echo "Setting up services and inittab"
for rc in boot/bootmisc boot/hostname boot/modules boot/sysctl boot/urandom boot/networking \
	sysinit/devfs sysinit/hwdrivers sysinit/mdev sysinit/modules \
	shutdown/mount-ro shutdown/killprocs \
	default/dbus default/chronyd; do
	ln -s /etc/init.d/"${rc##*/}" "$TMPDIR"/etc/runlevels/"$rc"
done

echo "preparing scripts for setup"
wget -O $TMPDIR/root/update-vendor-firmware  https://dev.alpinelinux.org/~mps/m1/update-vendor-firmware
wget -O $TMPDIR/etc/iwd/main.conf https://dev.alpinelinux.org/~mps/m1/iwd-main.conf

cat > "$TMPDIR"/root/grub-setup.sh<<EOF
#!/bin/sh
grub-install --target=arm64-efi --efi-directory=/boot/efi --removable
grub-mkfont -s 24 -o /boot/grub/fonts/dejavu.pf2 /usr/share/fonts/ttf-dejavu/DejaVuSansMono.ttf
grub-mkconfig  > /boot/grub/grub.cfg
exit
EOF

echo 'GRUB_DISABLE_LINUX_PARTUUID=false' >> "$TMPDIR"/etc/default/grub
echo 'GRUB_CMDLINE_LINUX="rootwait console=tty1"' >> "$TMPDIR"/etc/default/grub
echo 'GRUB_TIMEOUT=10' >> "$TMPDIR"/etc/default/grub
echo 'GRUB_FONT="/boot/grub/fonts/dejavu.pf2"' >> "$TMPDIR"/etc/default/grub
chmod 0744 "$TMPDIR"/root/grub-setup.sh

echo "Settting grub boot loader"
mount -t proc none $TMPDIR/proc
mount -o bind /sys $TMPDIR/sys
mount -o bind /dev $TMPDIR/dev
echo "Run this two commands and after that exit"
echo "grub-install --target=arm64-efi --efi-directory=/boot/efi --removable"
echo "grub-mkconfig  > /boot/grub/grub.cfg"
chroot $TMPDIR /root/grub-setup.sh
#chroot $TMPDIR /bin/ash -l
umount $TMPDIR/dev
umount $TMPDIR/sys
umount $TMPDIR/proc

sed -i 's/^tty1.*/tty1::respawn:\/sbin\/agetty -L 115200 tty1 linux --login-pause --autologin root --noclear/' $TMPDIR/etc/inittab
echo "to install apple wifi firmware you can 'sh update-vendor-firmware'" >> $TMPDIR//etc/motd
echo "and then '/etc/init.d/iwd start' to start wifi" >> $TMPDIR/etc/motd

echo "Finished, cleaning up"


script bellow will install Alpine Linux aarch64 edge to run on usb disk on apple M1

script is created with big help of Alpine Linux developer Carlo

size of disk image should be adjusted according needs and parameters for parted to create partitions of the needed sizes

prerequisite on alpine linux:

qemu-openrc must be installed  if running on machine which is not aarch64
util-linux or util-linux-misc
e2fsprogs
qemu-aarch64

start this before running install script
/etc/init.d/qemu-binfmt start

if runing on non Alpine linux distros apk-tools-static must be installed

apk-tool-static could be downloaded from https://dl-cdn.alpinelinux.org/alpine/edge/main/x86_64/ and unpacked with tar
tar xf apk-tool-static-$version-$release.apk

create bootable usb flash/disk for M1 running this script, be careful and set $DISK to usb disk on which you want to install alpine

script can be invoked by DISK=/dev/sdY ./install.m1 where Y is usb disk to which alpine should be installed

ash shell script used to create bootable usb disk (it can be customized to increase filesystem size, install extra packages, firmware set different ooptions etc...)

#!/bin/sh

# apk add blkid parted dosfstools e2fsprogs wget tar
set -eu

: "${DISK:=/dev/sdX}"
if [ $DISK = "/dev/sdX" ]; then
  echo "DISK is not set correctly"
  exit
fi

readonly SCRIPT="${0##*/}"
readonly TMPDIR="$(mktemp -dt "${SCRIPT%.*}".XXXXXX)"
readonly BOOTDEV="$DISK"1
readonly ROOTDEV="$DISK"2
readonly APK_KEY="alpine-devel@lists.alpinelinux.org-60ac2099.rsa.pub"
readonly MIRROR="http://dl-cdn.alpinelinux.org/alpine"

cleanup() {
	set +e
	mountpoint -q "$TMPDIR"/boot/efi && umount "$TMPDIR"/boot/efi
	mountpoint -q "$TMPDIR" && umount "$TMPDIR"
	rm -rf "$TMPDIR"
}

trap cleanup EXIT INT

echo "Paritioning disk"
# fat32 64MB, ext4 200MB
parted -s "$DISK" mktable gpt > /dev/null
parted -s "$DISK" unit s -- mkpart primary fat32 2048 133119 > /dev/null
parted -s "$DISK" unit s -- mkpart primary ext4 133120 542719 > /dev/null
parted -s "$DISK" -- set 1 esp on > /dev/null

echo "Creating dirs and mounting"
mkfs.fat -F 32 -n ESP "$BOOTDEV"
mkfs.ext4 -qL root "$ROOTDEV"
mount -t ext4 "$ROOTDEV" "$TMPDIR"
mkdir -p "$TMPDIR"/boot
mkdir -p "$TMPDIR"/boot/efi "$TMPDIR"/etc/apk/keys
mount -t vfat "$BOOTDEV" "$TMPDIR"/boot/efi

echo "Setup apk keys and repositories"
wget -qP "$TMPDIR"/etc/apk/keys https://alpinelinux.org/keys/"$APK_KEY"
printf "$MIRROR/edge/%s\n" main community testing > "$TMPDIR"/etc/apk/repositories

echo "installing base packages"
apk --allow-untrusted --root "$TMPDIR" --arch aarch64 --initdb add \
	alpine-base alpine-baselayout alpine-conf kmod openrc \
	dbus util-linux blkid chrony \
	sysfsutils ssl_client ca-certificates-bundle alpine-keys \
	ethtool e2fsprogs libudev-zero libudev-zero-helper \
	iwd linux-firmware-none efibootmgr ttf-dejavu \
	nvme-cli agetty terminus-font openresolv tar wget
apk --allow-untrusted --root "$TMPDIR" --arch aarch64 --no-script add linux-asahi
apk --allow-untrusted --root "$TMPDIR" --arch aarch64 --no-script add grub-efi grub-mkfont

echo "Setting up services and inittab"
for rc in boot/bootmisc boot/hostname boot/modules boot/sysctl boot/urandom boot/networking \
	sysinit/devfs sysinit/hwdrivers sysinit/mdev sysinit/modules \
	shutdown/mount-ro shutdown/killprocs \
	default/dbus default/chronyd; do
	ln -s /etc/init.d/"${rc##*/}" "$TMPDIR"/etc/runlevels/"$rc"
done

echo "preparing scripts for setup"
wget -O $TMPDIR/root/update-vendor-firmware  https://dev.alpinelinux.org/~mps/m1/update-vendor-firmware
wget -O $TMPDIR/root/find-efi.sh https://dev.alpinelinux.org/~mps/m1/find-efi.sh
wget -O $TMPDIR/root/rmmod.sh https://dev.alpinelinux.org/~mps/m1/rmmod.sh
wget -O $TMPDIR/etc/iwd/main.conf https://dev.alpinelinux.org/~mps/m1/iwd-main.conf
wget -O $TMPDIR/root/install-nvme.sh https://dev.alpinelinux.org/~mps/m1/install-nvme.sh
echo 'blacklist brcmfmac' >>  $TMPDIR/etc/modprobe.d/blacklist-brcmfmac.conf

cat > "$TMPDIR"/root/grub-setup.sh<<EOF
#!/bin/sh
grub-install --target=arm64-efi --efi-directory=/boot/efi --removable
grub-mkfont -s 24 -o /boot/grub/fonts/dejavu.pf2 /usr/share/fonts/ttf-dejavu/DejaVuSansMono.ttf
grub-mkconfig  > /boot/grub/grub.cfg
exit
EOF

echo 'GRUB_DISABLE_LINUX_PARTUUID=false' >> "$TMPDIR"/etc/default/grub
echo 'GRUB_CMDLINE_LINUX="rootwait console=tty1"' >> "$TMPDIR"/etc/default/grub
echo 'GRUB_TIMEOUT=10' >> "$TMPDIR"/etc/default/grub
echo 'GRUB_FONT="/boot/grub/fonts/dejavu.pf2"' >> "$TMPDIR"/etc/default/grub
chmod 0744 "$TMPDIR"/root/grub-setup.sh

echo "Settting grub boot loader"
mount -t proc none $TMPDIR/proc
mount -o bind /sys $TMPDIR/sys
mount -o bind /dev $TMPDIR/dev
echo "Run this two commands and after that exit"
echo "grub-install --target=arm64-efi --efi-directory=/boot/efi --removable"
echo "grub-mkconfig  > /boot/grub/grub.cfg"
chroot $TMPDIR /root/grub-setup.sh
#chroot $TMPDIR /bin/ash -l
umount $TMPDIR/dev
umount $TMPDIR/sys
umount $TMPDIR/proc

sed -i 's/^tty1.*/tty1::respawn:\/sbin\/agetty -L 115200 tty1 linux --login-pause --autologin root --noclear/' $TMPDIR/etc/inittab
echo "to install apple wifi firmware you can 'sh update-vendor-firmware'" >> $TMPDIR//etc/motd
echo "and then '/etc/init.d/iwd start' to start wifi" >> $TMPDIR/etc/motd

echo "Finished, cleaning up"