Multi-boot partitioning / Void Linux installation via chroot
Why void-installer doesn’t cut it
void-installer is a curses-based installer that handles straightforward setups: one root partition, one /home, standard /boot. It cannot target an existing btrfs pool and lay subvolumes into it, cannot share a /boot partition that already exists, and cannot configure fstab with the subvolume flags needed for this layout.
Manual installation via chroot is not harder — it’s just more explicit. Every step that a graphical installer hides becomes a line you type. Nothing happens that you didn’t intend.
Mount the target layout
All commands run from a Void live ISO. The target btrfs pool (p5, UUID d7a98795-1604-4baa-ae07-05e29a3ff1e6) and subvolumes must already exist.
# create subvolumes first if they don't exist
mount /dev/nvme0n1p5 /mnt
btrfs subvolume create /mnt/@void
btrfs subvolume create /mnt/@void-var
btrfs subvolume create /mnt/@void-snapshots
umount /mnt
# mount in the correct order
mount -o subvol=@void,compress=zstd:3,noatime /dev/nvme0n1p5 /mnt
mkdir -p /mnt/{var,boot,.snapshots}
mount -o subvol=@void-var,compress=zstd:3,noatime /dev/nvme0n1p5 /mnt/var
mount -o subvol=@void-snapshots,compress=zstd:3,noatime /dev/nvme0n1p5 /mnt/.snapshots
mount /dev/nvme0n1p8 /mnt/boot
mkdir -p /mnt/boot/efi
mount /dev/nvme0n1p1 /mnt/boot/efi
Install the base system
xbps-install -Sy -R https://repo-default.voidlinux.org/current -r /mnt base-system
base-system pulls in the kernel, dracut, and the core userspace. The -r /mnt flag sets the target root.
Enter the chroot
mount --rbind /sys /mnt/sys
mount --rbind /dev /mnt/dev
mount --rbind /proc /mnt/proc
chroot /mnt /bin/bash
Inside the chroot, set the shell properly:
source /etc/profile
export PS1="(chroot) $PS1"
Configure the system
# hostname
echo "yourhostname" > /etc/hostname
# locale
echo "en_US.UTF-8 UTF-8" >> /etc/default/libc-locales
xbps-reconfigure -f glibc-locales
# timezone
ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime
# root password
passwd root
# create user
useradd -m -G wheel,audio,video,plugdev -s /bin/zsh younes
passwd younes
Write fstab
cat > /etc/fstab << 'EOF'
UUID=d7a98795-1604-4baa-ae07-05e29a3ff1e6 / btrfs subvol=@void,compress=zstd:3,noatime 0 0
UUID=d7a98795-1604-4baa-ae07-05e29a3ff1e6 /var btrfs subvol=@void-var,compress=zstd:3,noatime 0 0
UUID=d7a98795-1604-4baa-ae07-05e29a3ff1e6 /.snapshots btrfs subvol=@void-snapshots,compress=zstd:3,noatime 0 0
UUID=d7a98795-1604-4baa-ae07-05e29a3ff1e6 /home btrfs subvol=@home,compress=zstd:3,noatime 0 0
UUID=d7a98795-1604-4baa-ae07-05e29a3ff1e6 /data btrfs subvol=@data,compress=zstd:3,noatime 0 0
UUID=d7a98795-1604-4baa-ae07-05e29a3ff1e6 /var/lib/docker btrfs subvol=@docker,nodatacow,noatime 0 0
UUID=fba989bd-2a90-4e11-9aa2-4c941d3e0460 /boot ext4 defaults 0 1
UUID=1A4A-CE0B /boot/efi vfat defaults 0 2
EOF
The dracut btrfs trap
This is the failure mode that produces a kernel panic on first boot with no obvious error message. dracut builds the initramfs, and by default the btrfs module is not included in the initramfs on some builds.
Without the btrfs module in the initramfs, the kernel mounts the initramfs successfully, then cannot find the btrfs root partition, and panics.
Fix:
mkdir -p /etc/dracut.conf.d
echo 'add_dracutmodules+=" btrfs "' > /etc/dracut.conf.d/btrfs.conf
Then regenerate the initramfs:
xbps-reconfigure -fa
xbps-reconfigure -fa runs the post-install scripts for all packages, which includes regenerating initramfs images for every installed kernel. Without this, the config file exists but does nothing.
Verify the module is present in the initramfs:
lsinitrd /boot/initramfs-6.12.81_1.img | grep btrfs
GRUB entry
Do not run grub-install inside the Void chroot. Arch owns GRUB. Instead, add Void as a manual entry in Arch’s /etc/grub.d/40_custom:
menuentry "Void Linux" {
insmod part_gpt
insmod btrfs
insmod ext2
set root='hd0,gpt8'
linux /vmlinuz-6.12.81_1 root=UUID=d7a98795-1604-4baa-ae07-05e29a3ff1e6 rootflags=subvol=@void rw quiet
initrd /initramfs-6.12.81_1.img
}
Then from Arch:
grub-mkconfig -o /boot/grub/grub.cfg
See [Multi-boot partitioning / Shared GRUB partition] for why this architecture exists.
xbps vs pacman, for Arch users
| Operation | pacman | xbps |
|---|---|---|
| Install package | pacman -S pkg |
xbps-install pkg |
| Remove package | pacman -R pkg |
xbps-remove pkg |
| Search | pacman -Ss term |
xbps-query -Rs term |
| List installed | pacman -Q |
xbps-query -l |
| Sync repos | pacman -Sy |
xbps-install -S |
| Full upgrade | pacman -Syu |
xbps-install -Su |
| Reconfigure pkg | (post-install hooks) | xbps-reconfigure pkg |
There is no AUR equivalent. The Void package repository is smaller than Arch’s. What doesn’t exist in the repo requires building from source via the void-packages tree, using a XBPS source package (xbps-src).
runit vs systemd
Void uses runit, not systemd. Services are enabled by symlinking the service directory into /var/service/:
# enable a service
ln -s /etc/sv/sshd /var/service/
# check status
sv status sshd
# restart
sv restart sshd
Service definitions live in /etc/sv/. Each service directory contains a run script (executable), optionally a log/run script for logging, and optionally a finish script. runit checks /var/service/ every five seconds and starts any new symlinks it finds.
The practical difference from systemd: no unit file syntax, no systemctl daemon-reload, no journal queries for service logs. Logs go to per-service log directories or to syslog if a log runner is configured.
Connection closed. Returning to terminal...