Rocky Linux Root on ZFS
ZFSBootMenu
ZFSBootMenu is an alternative bootloader free of such limitations and has support for boot environments. Do not follow instructions on this page if you plan to use ZBM, as the layouts are not compatible. Refer to their site for installation details.
Customization
Unless stated otherwise, it is not recommended to customize system configuration before reboot.
Only use well-tested pool features
You should only use well-tested pool features. Avoid using new features if data integrity is paramount. See, for example, this comment.
UEFI support only
Only UEFI is supported by this guide.
Preparation
Disable Secure Boot. ZFS modules can not be loaded if Secure Boot is enabled.
Because the kernel of latest Live CD might be incompatible with ZFS, we will use Alpine Linux Extended, which ships with ZFS by default.
Download latest extended variant of Alpine Linux live image, verify checksum and boot from it.
gpg --auto-key-retrieve --keyserver hkps://keyserver.ubuntu.com --verify alpine-extended-*.asc dd if=input-file of=output-file bs=1M
Login as root user. There is no password.
Configure Internet
setup-interfaces -r # You must use "-r" option to start networking services properly # example: network interface: wlan0 WiFi name: <ssid> ip address: dhcp <enter done to finish network config> manual netconfig: n
If you are using wireless network and it is not shown, see Alpine Linux wiki for further details.
wpa_supplicant
can be installed withapk add wpa_supplicant
without internet connection.Configure SSH server
setup-sshd # example: ssh server: openssh allow root: "prohibit-password" or "yes" ssh key: "none" or "<public key>"
Set root password or
/root/.ssh/authorized_keys
.Connect from another computer
ssh root@192.168.1.91
Configure NTP client for time synchronization
setup-ntp busybox
Set up apk-repo. A list of available mirrors is shown. Press space bar to continue
setup-apkrepos
Throughout this guide, we use predictable disk names generated by udev
apk update apk add eudev setup-devd udev
Target disk
List available disks with
find /dev/disk/by-id/
If virtio is used as disk bus, power off the VM and set serial numbers for disk. For QEMU, use
-drive format=raw,file=disk2.img,serial=AaBb
. For libvirt, edit domain XML. See this page for examples.Declare disk array
DISK='/dev/disk/by-id/ata-FOO /dev/disk/by-id/nvme-BAR'
For single disk installation, use
DISK='/dev/disk/by-id/disk1'
Set a mount point
MNT=$(mktemp -d)
Set partition size:
Set swap size in GB, set to 1 if you don’t want swap to take up too much space
SWAPSIZE=4
Set how much space should be left at the end of the disk, minimum 1GB
RESERVE=1
Install ZFS support from live media:
apk add zfs
Install partition tool
apk add parted e2fsprogs cryptsetup util-linux
System Installation
Partition the disks.
Note: you must clear all existing partition tables and data structures from target disks.
For flash-based storage, this can be done by the blkdiscard command below:
partition_disk () { local disk="${1}" blkdiscard -f "${disk}" || true parted --script --align=optimal "${disk}" -- \ mklabel gpt \ mkpart EFI 1MiB 4GiB \ mkpart rpool 4GiB -$((SWAPSIZE + RESERVE))GiB \ mkpart swap -$((SWAPSIZE + RESERVE))GiB -"${RESERVE}"GiB \ set 1 esp on \ partprobe "${disk}" } for i in ${DISK}; do partition_disk "${i}" done
Setup temporary encrypted swap for this installation only. This is useful if the available memory is small:
for i in ${DISK}; do cryptsetup open --type plain --key-file /dev/random "${i}"-part3 "${i##*/}"-part3 mkswap /dev/mapper/"${i##*/}"-part3 swapon /dev/mapper/"${i##*/}"-part3 done
Load ZFS kernel module
modprobe zfs
Create root pool
Unencrypted:
# shellcheck disable=SC2046 zpool create \ -o ashift=12 \ -o autotrim=on \ -R "${MNT}" \ -O acltype=posixacl \ -O canmount=off \ -O dnodesize=auto \ -O normalization=formD \ -O relatime=on \ -O xattr=sa \ -O mountpoint=none \ rpool \ mirror \ $(for i in ${DISK}; do printf '%s ' "${i}-part2"; done)
Create root system container:
# dracut demands system root dataset to have non-legacy mountpoint zfs create -o canmount=noauto -o mountpoint=/ rpool/root
Create system datasets, manage mountpoints with
mountpoint=legacy
zfs create -o mountpoint=legacy rpool/home zfs mount rpool/root mount -o X-mount.mkdir -t zfs rpool/home "${MNT}"/home
Format and mount ESP. Only one of them is used as /boot, you need to set up mirroring afterwards
for i in ${DISK}; do mkfs.vfat -n EFI "${i}"-part1 done for i in ${DISK}; do mount -t vfat -o fmask=0077,dmask=0077,iocharset=iso8859-1,X-mount.mkdir "${i}"-part1 "${MNT}"/boot break done
System Configuration
Download and extract minimal Rhel root filesystem:
apk add curl curl --fail-early --fail -L \ https://dl.rockylinux.org/vault/rocky/9.2/images/x86_64/Rocky-9-Container-Base-9.2-20230513.0.x86_64.tar.xz \ -o rootfs.tar.gz curl --fail-early --fail -L \ https://dl.rockylinux.org/vault/rocky/9.2/images/x86_64/Rocky-9-Container-Base-9.2-20230513.0.x86_64.tar.xz.CHECKSUM \ -o checksum # BusyBox sha256sum treats all lines in the checksum file # as checksums and requires two spaces " " # between filename and checksum grep 'Container-Base' checksum \ | grep '^SHA256' \ | sed -E 's|.*= ([a-z0-9]*)$|\1 rootfs.tar.gz|' > ./sha256checksum sha256sum -c ./sha256checksum tar x -C "${MNT}" -af rootfs.tar.gz
Enable community repo
sed -i '/edge/d' /etc/apk/repositories sed -i -E 's/#(.*)community/\1community/' /etc/apk/repositories
Generate fstab:
apk add arch-install-scripts genfstab -t PARTUUID "${MNT}" \ | grep -v swap \ | sed "s|vfat.*rw|vfat rw,x-systemd.idle-timeout=1min,x-systemd.automount,noauto,nofail|" \ > "${MNT}"/etc/fstab
Chroot
cp /etc/resolv.conf "${MNT}"/etc/resolv.conf for i in /dev /proc /sys; do mkdir -p "${MNT}"/"${i}"; mount --rbind "${i}" "${MNT}"/"${i}"; done chroot "${MNT}" /usr/bin/env DISK="${DISK}" bash
Unset all shell aliases, which can interfere with installation:
unalias -a
Install base packages
dnf -y install --allowerasing @core kernel-core kernel-modules
Install ZFS packages:
dnf install -y https://zfsonlinux.org/epel/zfs-release-2-3"$(rpm --eval "%{dist}"|| true)".noarch.rpm dnf config-manager --disable zfs dnf config-manager --enable zfs-kmod dnf install -y zfs zfs-dracut
Add zfs modules to dracut:
echo 'add_dracutmodules+=" zfs "' >> /etc/dracut.conf.d/zfs.conf echo 'force_drivers+=" zfs "' >> /etc/dracut.conf.d/zfs.conf
Add other drivers to dracut:
if grep mpt3sas /proc/modules; then echo 'force_drivers+=" mpt3sas "' >> /etc/dracut.conf.d/zfs.conf fi if grep virtio_blk /proc/modules; then echo 'filesystems+=" virtio_blk "' >> /etc/dracut.conf.d/fs.conf fi
Build initrd:
find -D exec /lib/modules -maxdepth 1 \ -mindepth 1 -type d \ -exec sh -vxc \ 'if test -e "$1"/modules.dep; then kernel=$(basename "$1"); dracut --verbose --force --kver "${kernel}"; fi' sh {} \;
For SELinux, relabel filesystem on reboot:
fixfiles -F onboot
Generate host id:
zgenhostid -f -o /etc/hostid
Install locale package, example for English locale:
dnf install -y glibc-minimal-langpack glibc-langpack-en
Set locale, keymap, timezone, hostname
rm -f /etc/localtime systemd-firstboot \ --force \ --locale=en_US.UTF-8 \ --timezone=Etc/UTC \ --hostname=testhost \ --keymap=us
Set root passwd
printf 'root:yourpassword' | chpasswd
Bootloader
Install rEFInd boot loader:
# from http://www.rodsbooks.com/refind/getting.html # use Binary Zip File option curl -L https://sourceforge.net/projects/refind/files/0.14.0.2/refind-bin-0.14.0.2.zip/download --output refind.zip dnf install -y unzip unzip refind.zip mkdir -p /boot/EFI/BOOT find ./refind-bin-0.14.0.2/ -name 'refind_x64.efi' -print0 \ | xargs -0I{} mv {} /boot/EFI/BOOT/BOOTX64.EFI rm -rf refind.zip refind-bin-0.14.0.2
Add boot entry:
tee -a /boot/refind-linux.conf <<EOF "Rocky Linux" "root=ZFS=rpool/root" EOF
Exit chroot
exit
Unmount filesystems and create initial system snapshot You can later create a boot environment from this snapshot. See Root on ZFS maintenance page.
umount -Rl "${MNT}" zfs snapshot -r rpool@initial-installation
Export all pools
zpool export -a
Reboot
reboot
Post installaion
Install package groups
dnf group list --hidden -v # query package groups dnf group install gnome-desktop
Add new user, configure swap.
Mount other EFI system partitions then set up a service for syncing their contents.