cd ~/>cat multi-boot-partitioning-void-chroot-install.md

Multi-boot partitioning / Void Linux installation via chroot

#linux#void#btrfs#chroot#dracut#grub#multiboot

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.

// END OF TRANSMISSION
See you, Space Cowboy.

Connection closed. Returning to terminal...