(Debian users who know what they’re doing: there’s probably nothing hugely amazing here, and instead just lots of horrors!)
I tried switching to Debian for a month or so, and did so broadly non-destructively to my original Arch environment. How did I do it?
Short answer: ZFS boot environments and debootstrap.
Long answer:
Create a boot environment.
With a function or script something like:
create() {
local dataset="$1"; shift;
local mountpoint="${dataset#lisbon/ROOT/debian}"
zfs create "$@" "$dataset";
mkdir -p "/lisbon/debian/${mountpoint}";
mount -t zfs "$dataset" "/lisbon/debian/${mountpoint}";
printf '%s %s zfs rw,xattr,posixacl 0 0\n' \
"${dataset}" \
"/lisbon/debian/${mountpoint%/}" \
>> /etc/fstab
}
I then do:
create lisbon/ROOT/debian -o mountpoint=legacy create lisbon/ROOT/debian/usr create lisbon/ROOT/debian/usr/local create lisbon/ROOT/debian/var create lisbon/ROOT/debian/var/cache -o com.sun:auto-snapshot=false create lisbon/ROOT/debian/var/lib create lisbon/ROOT/debian/var/log create lisbon/ROOT/debian/var/obj create lisbon/ROOT/debian/var/spool create lisbon/ROOT/debian/var/tmp
I’m not at all sure that the constellation of datasets in the OpenZFS install guide are needed at all: whilst that suggests their creation to make rollbacks easier, I’m not yet convinced, and I’ve got a fairly stripped down set. (I suspect this could go further.)
I initially also created datasets
for /opt
and /srv
,
which broke things a little;
but I backed them out.
I’m sure I shouldn’t need to futz around with explicitly mounting datasets, either. If someone’s got a better idea, let me know!
Run debootstrap.
Now we’ve got our system mounted, grab a copy of both debootstrap and debian-archive-keyring, and unroll them. (I’m not sure the latter is necessary: I’d also been trying cdebootstrap, which did need it, and my final debootstrap invocation looks to have included it.)
I use tar(1) from libarchive.
You could use dpkg-deb -x
,
if you can summon up dpkg
from your local package manager.
POOL=https://mirror.aarnet.edu.au/debian/pool wget \ $POOL/main/d/debian-archive-keyring/debian-archive-keyring_2021.1.1_all.deb \ $POOL/main/d/debootstrap/debootstrap_1.0.126+nmu1_all.deb [... wget output elided ...] for i (*.tar); tar xOf $i data.tar.xz | tar xvf - [... tar output elided ...]
Now, we run the bootstrap. Surprise! It’s super simple.
doas \ env DEBOOTSTRAP_DIR=$PWD/usr/share/debootstrap \ bash usr/sbin/debootstrap \ --keyring=usr/share/keyrings/debian-archive-keyring.gpg \ --arch=amd64 \ --components=main,contrib,non-free \ testing \ /lisbon/debian \ https://deb.debian.org/debian/ [...]
Now for the fun bits:
Do post-installation configuration.
Shovelling files from the host environment seems like a reasonable fit at times. For things like keys and network/device configuration, these are definitely easiest to just slurp from the host.
cd /lisbon/debian [Copy in the fstab(5) and fix it up.] cp -p /etc/fstab etc/fstab mg etc/fstab [Copy in a bunch of local configuration:] cp -p /etc/doas.conf etc/doas.conf cp -p /etc/sudoers etc/sudoers cp -p /etc/hosts etc/hosts cp -p /etc/localtime* etc/ cp -p /etc/ssh/*_key* etc/ssh cp -p /etc/zfs/* etc/zfs/ [Copy Bluetooth configuration and state:] rsync -Pai /etc/bluetooth/ etc/bluetooth rsync -Pai /var/lib/bluetooth/ var/lib/bluetooth rsync -Pai /var/lib/blueman/ var/lib/blueman [Copy print configuration:] rsync -Pai /etc/cups/ etc/cups [Copy firewall configuration:] rsync -Pai /etc/firewalld/ etc/firewalld [Copy network configuration and state:] rsync -Pai /etc/NetworkManager/ etc/NetworkManager rsync -Pai /var/lib/NetworkManager/ var/lib/NetworkManager [Copy sound system state:] rsync -Pai /var/lib/alsa/ var/lib/alsa
I then push in the rest of my configuration. This gives me a whole stack of stuff for free, including hostid, hostname, krb5.conf, locale.gen, modprobe.d, ntp.conf, systemd configuration, and some ZFS support scripts.
rsync -Pai --chown=root:root ~jashank/.system/_etc/ etc
(There’s almost definitely stuff missing that I’ve forgotten entirely, and which I’m sure I’ll remember eventually.)
Do more configuration; install packages.
This is the first time we get to actually see whether our system even works! I’m excited. (Well, I was the first time. After about the tenth, it wasn’t nearly as much fun.)
[strip comments from the package list.] ccat ~/etc/packages.debian.lisbon > /lisbon/debian/var/tmp/pkgs [drop into a new systemd slice] sudo systemd-nspawn --quiet --directory=/lisbon/debian --register=yes --as-pid2 apt update apt install $(cat /var/tmp/pkgs) [download the Internet...]
In particular, packages on this list include
zfs-initramfs
and zfsutils-linux
for ZFS,
grub-efi-amd64
so we can have some bootloader support glue,
a linux-image-amd64
to run, and some firmware.
There’s lots of other stuff too!
Futz with GRUB. (Optionally, break UEFI.)
I don’t use grub-mkconfig(8) so this is usually a fairly manual process.
At some point in the past, I wrote a GRUB function that looks like:
function load_linux {
set root=($zpool)/ROOT/$1/@
set kernel_img=$root/$2
set initrd_img=$root/$3
set rootparam=ZFS=lisbon/ROOT/$1
shift; shift; shift
echo ":: loading $kernel_img ... "
linux $kernel_img root=$rootparam rw resume=$l_resume "$@"
if [ -f "$root/boot/amd-ucode.img" ]
then
echo ":: loading $root/boot/amd-ucode.img ... "
initrd $root/boot/amd-ucode.img
fi
echo ":: loading $initrd_img ... "
initrd $initrd_img
echo ":: booting."
echo ""
}
I can now add invocations like:
menuentry '[lisbon] Debian Linux testing (linux)' \
--class debian --class gnu-linux --class gnu --class os \
{
load_linux debian vmlinuz initrd.img \
security=selinux
}
menuentry '[lisbon] Debian Linux testing (linux), fallback' \
--class debian --class gnu-linux --class gnu --class os \
{
load_linux debian vmlinuz.old initrd.img.old \
security=selinux
}
I also had a brief interlude where I accidentally wiped my UEFI boot sequence — efibootmgr(8) sure is easy to use! — and ended up needing the UEFI Shell to recover. That’s always a great adventure, and I’m very glad that the UEFI shell exists. Once I got back into my system, I ended up doing:
efibootmgr \ --bootnum 0000 \ --create \ --disk /dev/nvme0n1p1 --part 1 \ --loader /EFI/GRUB/shimx64.efi \ --label 'shimx64/grubx64'
Enjoy.
Oh.