Skip to content

Instantly share code, notes, and snippets.

@johnlane
Last active November 1, 2023 19:56
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save johnlane/e0e7b4d870b675ec1e304c8c8aa3201a to your computer and use it in GitHub Desktop.
Save johnlane/e0e7b4d870b675ec1e304c8c8aa3201a to your computer and use it in GitHub Desktop.
Simple installer for ArchLinux on ZFS (tested in VirtualBox)

ArchZFS examples

Use with QEMU; example:

for i in {1..2}; do qemu-img create -f qcow2 hd$i.qcow2 4G; done
qemu-system-x86_64 --enable-kvm -global isa-fdc.fdtypeA=none -cdrom archlinux-2023.07.25-x86_64.iso -nic bridge,br=br0 -m 8192 -smp cpus=4 -hda hd1.qcow2 --hdb hd2.qcow2 &

(or use VirtualBox or bare metal - change variables at top of scripts to specify disks)

ISO is the standard Arch ISO with ZFS added; you need an ISO with ZFS support.

Press <TAB> when the boot screen shows and append cow_spacesize=100% to the boot command. This allows the ramdisk to grow into the available memory as required. Press <ENTER> to boot.

When the system has booted, discover its IP address and set a root password:

$ ip address
$ passwd

Then (e.g. from your laptop) copy arch-zfsroot-install-X, where X is either init or systemd, and execute it:

$ scp arch-zfsroot-install-X root@192.168.1.1:
$ ssh root@192.168.1.1 ./arch-zfsroot-install-X

Finally, reboot into new rootfs (you can remove the ISO).

#!/bin/bash
#Install ZFS rootfs on Arch Linux
#JL20180619 revised JL20230729
# Parameters - adjust accordingly
ZPOOL=tank
RAIDZ=mirror
DISKS=( /dev/disk/by-id/ata-QEMU_HARDDISK_QM0000{1,2} )
# Ensure zpool offline
zpool list "$ZPOOL" &> /dev/null && zpool export "$ZPOOL" && zpool destroy "$ZPOOL"
# Zap disk partition tables
echo "${DISKS[@]}" | xargs -n 1 sgdisk -Z
# Create a new zpool; use -O to set default dataset preferences
# The altroot is non-persistent - just used during installation
zpool create -m none "$ZPOOL" "$RAIDZ" "${DISKS[@]}" \
-o altroot="/$ZPOOL" \
-O xattr=sa \
-O acltype=posix \
-O atime=off \
-O compression=lz4 \
-O recordsize=64k
# Create a dataset
zfs create -p "$ZPOOL"/ROOT/archlinux -o mountpoint=/
# Install OS to the zpool
echo installing...
pacstrap /"$ZPOOL"
# Continue install inside chroot
# Add ZFS dependencies (including compatible Linux kernel) and rebuild the initramfs
arch-chroot "/$ZPOOL" << EOF
echo -e '[archzfs]\nSigLevel = Optional TrustAll\nServer = http://archzfs.com/\$repo/\$arch' >> /etc/pacman.conf
pacman -Sy --noconfirm
pacman -S --noconfirm mkinitcpio
pacman -U --noconfirm "https://archive.archlinux.org/packages/l/linux/linux-$(uname -r | sed 's/-/./')-x86_64.pkg.tar.zst"
sed -i -e '/^HOOKS=/s/filesystems.*)/keyboard zfs filesystems)/' /etc/mkinitcpio.conf
pacman -S --noconfirm zfs-linux
cp /usr/lib/modules/\$(uname -r)/vmlinuz /boot/vmlinuz-linux
mkinitcpio -p linux
zpool set cachefile=/etc/zfs/zpool.cache "$ZPOOL"
echo "root:root" | chpasswd
exit
EOF
#
# Essentials
# Optional setup below
# Configure network, ssh and the ubiquitous vim
#
arch-chroot "/$ZPOOL" << EOF
nic="$(basename /sys/class/net/en*)"
echo -e "[Match]\nName=\$nic\n[Network]\nDHCP=yes" > "/etc/systemd/network/20-\$nic-dhcp.network"
systemctl enable systemd-{network,resolve}d
pacman --noconfirm -S openssh vim
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
systemctl enable sshd
EOF
# add BIOS boot partition to the disks (required by Grub)
for disk in "${DISKS[@]}"
do
sgdisk -n 2:34:2047 -t 2:EF02 "$disk" -c 2:"BIOS Boot Partition"
partx -u "$disk"
done
# Install Grub
echo "${DISKS[@]}" | ZPOOL_VDEV_NAME_PATH=1 xargs -n 1 grub-install --root-directory="/$ZPOOL"
# Write basic grub configuration to boot the zpool
cat > "/$ZPOOL/boot/grub/grub.cfg" << EOF
insmod part_gpt
menuentry 'Arch Linux ZFS' {
insmod part_gpt
echo 'Searching ...'
search --set --label "$ZPOOL"
echo 'Loading kernel ...'
linux /ROOT/archlinux@/boot/vmlinuz-linux zfs="$ZPOOL" rw
echo 'Loading initramfs ...'
initrd /ROOT/archlinux@/boot/initramfs-linux.img
echo 'Booting ...'
}
EOF
#!/bin/bash
#Install ZFS rootfs on Arch Linux with systemd initrd
#JL20180619 revised JL20230729
# Parameters - adjust accordingly
ZPOOL=tank
RAIDZ=mirror
DISKS=( /dev/disk/by-id/ata-QEMU_HARDDISK_QM0000{1,2} )
# Ensure zpool offline
zpool list "$ZPOOL" &> /dev/null && zpool export "$ZPOOL" && zpool destroy "$ZPOOL"
# Zap disk partition tables
echo "${DISKS[@]}" | xargs -n 1 sgdisk -Z
# Create a new zpool; use -O to set default dataset preferences
# The altroot is non-persistent - just used during installation
zpool create -m none "$ZPOOL" "$RAIDZ" "${DISKS[@]}" \
-o altroot="/$ZPOOL" \
-O xattr=sa \
-O acltype=posix \
-O atime=off \
-O compression=lz4 \
-O recordsize=64k
# Create a dataset
zfs create -p "$ZPOOL"/ROOT/archlinux -o mountpoint=/
# Install OS to the zpool
echo installing...
pacstrap /"$ZPOOL"
# Build systemd ZFS hook
pacman --noconfirm -Sy make gcc fakeroot
cd /tmp
curl https://aur.archlinux.org/cgit/aur.git/snapshot/mkinitcpio-sd-zfs.tar.gz | tar zx
chown -R nobody: mkinitcpio-sd-zfs
cd mkinitcpio-sd-zfs
sudo -u nobody makepkg
cp /tmp/mkinitcpio-sd-zfs/mkinitcpio-sd-zfs-1.0.2-1-any.pkg.tar.zst "/$ZPOOL/"
arch-chroot "/$ZPOOL" pacman --noconfirm -U /mkinitcpio-sd-zfs-1.0.2-1-any.pkg.tar.zst
rm "/$ZPOOL/"mkinitcpio-sd-zfs-1.0.2-1-any.pkg.tar.zst
cd $OLDPWD
# Continue install inside chroot
# Add ZFS dependencies (including compatible Linux kernel) and rebuild the initramfs
arch-chroot "/$ZPOOL" << EOF
echo -e '[archzfs]\nSigLevel = Optional TrustAll\nServer = http://archzfs.com/\$repo/\$arch' >> /etc/pacman.conf
pacman -Sy --noconfirm
pacman -S --noconfirm mkinitcpio
pacman -U --noconfirm "https://archive.archlinux.org/packages/l/linux/linux-$(uname -r | sed 's/-/./')-x86_64.pkg.tar.zst"
sed -i -e '/^HOOKS=(/s/udev/systemd/' \
-e '/^HOOKS=/s/filesystems.*)/sd-zfs)/' \
/etc/mkinitcpio.conf
pacman -S --noconfirm zfs-linux
cp /usr/lib/modules/\$(uname -r)/vmlinuz /boot/vmlinuz-linux
systemctl preset \$(tail -n +2 /usr/lib/systemd/system-preset/50-zfs.preset | cut -d ' ' -f 2)
mkinitcpio -p linux
zpool set cachefile=/etc/zfs/zpool.cache "$ZPOOL"
echo "root:root" | chpasswd
exit
EOF
#
# Essentials
# Optional setup below
# Configure network, ssh and the ubiquitous vim
#
arch-chroot "/$ZPOOL" << EOF
nic="$(basename /sys/class/net/en*)"
echo -e "[Match]\nName=\$nic\n[Network]\nDHCP=yes" > "/etc/systemd/network/20-\$nic-dhcp.network"
systemctl enable systemd-{network,resolve}d
pacman --noconfirm -S openssh vim
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
systemctl enable sshd
EOF
# add BIOS boot partition to the disks (required by Grub)
for disk in "${DISKS[@]}"
do
sgdisk -n 2:34:2047 -t 2:EF02 "$disk" -c 2:"BIOS Boot Partition"
partx -u "$disk"
done
# Install Grub
echo ${DISKS[@]} | ZPOOL_VDEV_NAME_PATH=1 xargs -n 1 grub-install --root-directory="/$ZPOOL"
# Write basic grub configuration to boot the zpool
cat > "/$ZPOOL/boot/grub/grub.cfg" << EOF
insmod part_gpt
menuentry 'Arch Linux ZFS' {
insmod part_gpt
echo 'Searching ...'
search --set --label "$ZPOOL"
echo 'Loading kernel ...'
linux /ROOT/archlinux@/boot/vmlinuz-linux root="zfs:$ZPOOL/ROOT/archlinux" rw
echo 'Loading initramfs ...'
initrd /ROOT/archlinux@/boot/initramfs-linux.img
echo 'Booting ...'
}
EOF
# Set mountpoint as expected by sd-zfs
zfs set mountpoint=none org.zol:mountpoint=/ "$ZPOOL"/ROOT/archlinux
# Export the pool so bootloader can import it
zpool export "$ZPOOL"
#!/bin/bash
#Install ZFS rootfs on Arch Linux with systemd initrd
#
# This is an example that creates two rootfs to demonstrate
# the problem caused by having multiple datasets with their
# mountpoint set to / (plus alternative config to fix it)
#
#JL20180619 revised JL20230729
# Parameters - adjust accordingly
ZPOOL=tank
RAIDZ=mirror
DISKS=( /dev/disk/by-id/ata-QEMU_HARDDISK_QM0000{1,2} )
# Ensure zpool offline
zpool list "$ZPOOL" &> /dev/null && zpool export "$ZPOOL" && zpool destroy "$ZPOOL"
# Zap disk partition tables
echo "${DISKS[@]}" | xargs -n 1 sgdisk -Z
# Create a new zpool; use -O to set default dataset preferences
# The altroot is non-persistent - just used during installation
zpool create -m none "$ZPOOL" "$RAIDZ" "${DISKS[@]}" \
-O xattr=sa \
-O acltype=posix \
-O atime=off \
-O compression=lz4 \
-O recordsize=64k
# Create a dataset
zfs create -p "$ZPOOL"/ROOT/root1 -o mountpoint=/root1
zfs create -p "$ZPOOL"/ROOT/root2 -o mountpoint=/root2
# Install OS to the zpool
echo installing...
pacstrap /root1
pacstrap /root2
# Build systemd ZFS hook
pacman --noconfirm -Sy make gcc fakeroot
cd /tmp
curl https://aur.archlinux.org/cgit/aur.git/snapshot/mkinitcpio-sd-zfs.tar.gz | tar zx
chown -R nobody: mkinitcpio-sd-zfs
cd mkinitcpio-sd-zfs
sudo -u nobody makepkg
cp /tmp/mkinitcpio-sd-zfs/mkinitcpio-sd-zfs-1.0.2-1-any.pkg.tar.zst /root1
arch-chroot /root1 pacman --noconfirm -U /mkinitcpio-sd-zfs-1.0.2-1-any.pkg.tar.zst
cp /tmp/mkinitcpio-sd-zfs/mkinitcpio-sd-zfs-1.0.2-1-any.pkg.tar.zst /root2
arch-chroot /root2 pacman --noconfirm -U /mkinitcpio-sd-zfs-1.0.2-1-any.pkg.tar.zst
cd $OLDPWD
# Continue install inside chroot
# Add ZFS dependencies (including compatible Linux kernel) and rebuild the initramfs
for r in 1 2; do
arch-chroot "/root$r" << EOF
echo -e '[archzfs]\nSigLevel = Optional TrustAll\nServer = http://archzfs.com/\$repo/\$arch' >> /etc/pacman.conf
pacman -Sy --noconfirm
pacman -S --noconfirm mkinitcpio
pacman -U --noconfirm "https://archive.archlinux.org/packages/l/linux/linux-$(uname -r | sed 's/-/./')-x86_64.pkg.tar.zst"
sed -i -e '/^HOOKS=(/s/udev/systemd/' \
-e '/^HOOKS=/s/filesystems.*)/sd-zfs)/' \
/etc/mkinitcpio.conf
pacman -S --noconfirm zfs-linux
cp /usr/lib/modules/\$(uname -r)/vmlinuz /boot/vmlinuz-linux
systemctl preset \$(tail -n +2 /usr/lib/systemd/system-preset/50-zfs.preset | cut -d ' ' -f 2)
mkinitcpio -p linux
zpool set cachefile=/etc/zfs/zpool.cache "$ZPOOL"
echo "root:root" | chpasswd
exit
EOF
done
#
# Essentials
# Optional setup below
# Configure network, ssh and the ubiquitous vim
#
for r in 1 2; do
arch-chroot "/root$r" << EOF
nic="$(basename /sys/class/net/en*)"
echo -e "[Match]\nName=\$nic\n[Network]\nDHCP=yes" > "/etc/systemd/network/20-\$nic-dhcp.network"
systemctl enable systemd-{network,resolve}d
pacman --noconfirm -S openssh vim
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
systemctl enable sshd
echo "root$r.example.com" > /etc/hostname
EOF
done
# add BIOS boot partition to the disks (required by Grub)
for disk in "${DISKS[@]}"
do
sgdisk -n 2:34:2047 -t 2:EF02 "$disk" -c 2:"BIOS Boot Partition"
partx -u "$disk"
done
# Install Grub
echo ${DISKS[@]} | ZPOOL_VDEV_NAME_PATH=1 xargs -n 1 grub-install --root-directory="/root1"
echo ${DISKS[@]} | ZPOOL_VDEV_NAME_PATH=1 xargs -n 1 grub-install --root-directory="/root2"
# Write basic grub configuration to boot the zpool
for r in 1 2; do
cat > "/root$r/boot/grub/grub.cfg" << EOF
insmod part_gpt
menuentry 'Arch Linux ZFS root1' {
echo 'Searching ...'
search --set --label "$ZPOOL"
echo 'Loading kernel ...'
linux /ROOT/root1@/boot/vmlinuz-linux root="zfs:$ZPOOL/ROOT/root1" rw
echo 'Loading initramfs ...'
initrd /ROOT/root1@/boot/initramfs-linux.img
echo 'Booting ...'
}
menuentry 'Arch Linux ZFS root2' {
echo 'Searching ...'
search --set --label "$ZPOOL"
echo 'Loading kernel ...'
linux /ROOT/root2@/boot/vmlinuz-linux root="zfs:$ZPOOL/ROOT/root2" rw
echo 'Loading initramfs ...'
initrd /ROOT/root2@/boot/initramfs-linux.img
echo 'Booting ...'
}
EOF
done
#
# UNCOMMENT THE BELOW TWO zfs set COMMANDS TO ALLOW BOOT TO WORK
# BOOT DOES NOT WORK IF MULTIPLE DATASETS HAVE "mountpoint=/"
#
#zfs set mountpoint=none org.zol:mountpoint=/ "$ZPOOL"/ROOT/root1
#zfs set mountpoint=none org.zol:mountpoint=/ "$ZPOOL"/ROOT/root2
# Export the pool so bootloader can import it
zpool export "$ZPOOL"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment