Ubuntu debootstrap for IoT maas router (Internet Gateway/firewall) – a 2-part post.
Part 1: Installing Ubuntu to Thin-lvm in a Very Arch-Like Way
A quick mention: I just copied this out of my plain text notes I do while completing these things. I think it’s fairly error-free, but I’ll come back and flesh it out a bit narratively later.
Any time following along on something like this, don’t forget to change the device names in places like /dev/mapper/yourvg-yourlv
etc. to ones that are appropriate for your system. The ones listed herein are appropriate for mine, which conversely also means not necessarily yours.
I love Arch to death and set up a laptop as a router using it, but I also think maas
is really awesome, and would love to have it on my internet gateway, so this is the start of me setting up Ubuntu (the only officially-supported platform for maas
) in a way that allows me to use some of the benefits of thin-lvm, like snapper
snapshots and potentially over-committed storage volumes (or, more realistically, volumes are less static/encumbered than raw partitions), while still retaining the snappiness of a journaled filesystem instead of a CoW FS, the latter of which tend to have irritatingly high latency, particularly with small files and/or little free space, not to mention growing worse over time.
Big thanks to Ansemjo for publishing his how-to for bootstrapping Ubuntu in order to set up LUKS, which was my main source for my adaptation to this guide – his original instructions are here: https://semjonov.de/posts/2021-09/minimal-ubuntu-installation-with-debootstrap/
I have 2x 22GB (useable) Intel 313 SLC SSDs on a RAID-1 mSATA controller I will cat
this lv to after it’s created
lvcreate -V 22G -T vms/pool -n maasrouter
Create VM in virt-manager with LV maasrouter as /dev/vda, boot from Ubuntu 24.04 desktop live iso
(many screenshots of virt-manager taken @ Aug 12 2024)
set display to VGA, turn off spice listen address, boot using ‘Safe Graphics’ GRUB option
Exit live ISO installer, open terminal, update, install the two required dependencies
ctrl-alt-T, sudo su
apt update && apt install -y debootstrap arch-install-scripts
A quick, garbage-free way to view connected block devices – set it as an alias
alias lsbk="lsblk | grep -v -E 'loop|sr0'"
lsbk
I had a machine lock up on me while installing the kernel. A quick get-back if the VM dies if fstab is already created [after a hard-reboot];
apt update && apt install -y arch-install-scripts
mount /dev/mrvg/mrrootlv /mnt
arch-chroot /mnt mount -a
example of lsbk
alias (I hate seeing loop
devices):
lsbk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 253:0 0 22G 0 disk
├─vda1 253:1 0 200M 0 part /efi
├─vda2 253:2 0 1G 0 part /boot
└─vda3 253:3 0 20.8G 0 part
├─mrvg-mrpool_tmeta 252:0 0 24M 0 lvm
│ └─mrvg-mrpool-tpool 252:2 0 20.8G 0 lvm
│ ├─mrvg-mrpool 252:3 0 20.8G 1 lvm
│ ├─mrvg-mrswaplv 252:4 0 4G 0 lvm
│ └─mrvg-mrrootlv 252:5 0 16G 0 lvm /
└─mrvg-mrpool_tdata 252:1 0 20.8G 0 lvm
└─mrvg-mrpool-tpool 252:2 0 20.8G 0 lvm
├─mrvg-mrpool 252:3 0 20.8G 1 lvm
├─mrvg-mrswaplv 252:4 0 4G 0 lvm
└─mrvg-mrrootlv 252:5 0 16G 0 lvm /
Prep drive and partitions:
sgdisk -g /dev/vda
fdisk /dev/vda
n, 1, enter, +200M
t, 1 (efi)
n, 2, enter, +1G
t, 2, 142 (xbootldr - "linux extended boot")
n, enter, enter, enter (rest of disk)
t, 3, 44 (LVM)
w (save and exit)
Create logical volumes and init filesystems:
mkfs.msdos -F 32 -n MR_EFI /dev/vda1
mkfs.ext4 -L MR_XBOOTLDR /dev/vda2
pvcreate /dev/vda3
vgcreate mrvg /dev/vda3
lvcreate -l 100%FREE -Zn -c 64 -T mrvg/mrpool
lvcreate -V 4G -T mrvg/mrpool -n mrswaplv
lvcreate -V 16G -T mrvg/mrpool -n mrrootlv
mkswap -L MR_SWAPLV /dev/mrvg/mrswaplv
mkfs.xfs -L MR_ROOTLV /dev/mrvg/mrrootlv
For this next step, you can replace ubuntu-server
with ubuntu-desktop
or vanilla-gnome-desktop
, as of writing apt search ubuntu-desktop
reveals several other options on a 24.04 desktop live ISO:
apt search ubuntu-desktop
Sorting... Done
Full Text Search... Done
edubuntu-desktop/noble 24.04.13 amd64
educational desktop for Ubuntu
edubuntu-desktop-minimal/noble 24.04.13 amd64
educational desktop for Ubuntu
kubuntu-desktop/noble 1.451 amd64
Kubuntu Plasma Desktop/Netbook system
lubuntu-desktop/noble 24.04.10 amd64
Lubuntu Desktop environment
ubuntu-desktop/noble 1.539 amd64
Ubuntu desktop system
ubuntu-desktop-minimal/noble 1.539 amd64
Ubuntu desktop minimal system
xubuntu-desktop/noble 2.262 amd64
Xubuntu desktop system
xubuntu-desktop-minimal/noble 2.262 amd64
Xubuntu minimal system
Mount FS structure and bootstrap:
mount /dev/mrvg/mrrootlv /mnt
mkdir -p /mnt/{boot,efi}
mount /dev/vda1 /mnt/efi
mount /dev/vda2 /mnt/boot
debootstrap --arch=amd64 \
--components=main,restricted,universe,multiverse \
--include=systemd-boot,systemd-boot-efi,arch-install-scripts,debootstrap,ubuntu-server,vim-scripts,vim-airline-themes,snapper,git,efibootmgr,bash-completion,tree,postgresql,pydf,thin-provisioning-tools,man-db,apt-file \
--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg \
noble /mnt https://mirror.leaseweb.com/ubuntu/
Copy host apt setup to VM FS:
cp /etc/apt/sources.list.d/ubuntu.sources /mnt/etc/apt/sources.list.d
Create /mnt/etc/kernel/cmdline
with a /dev/mapper
appropriate for you:
echo 'root=/dev/mapper/mrvg-mrrootlv rw rootfs=xfs loglevel=3 audit=0 intel_iommu=1 iommu.passthrough=1' > /mnt/etc/kernel/cmdline
Mount the swap before creating fstab
:
arch-chroot /mnt swapon -L MR_SWAPLV
Create your fstab:
genfstab -t PARTUUID /mnt > /mnt/etc/fstab
Delete the /mnt
from the swap line, change the EFI
to UUID
:
gnome-text-editor /mnt/etc/fstab
Here’s a finished example:
# <file system> <mount point> <type> <options> <dump> <pass>
# /dev/mapper/mrvg-mrrootlv UUID=37374f8c-a4bf-4577-96c0-1cd8a9fbe114 LABEL=MR_ROOTLV
/dev/mapper/mrvg-mrrootlv / xfs rw,relatime,attr2,inode64,logbufs=8,logbsize=64k,sunit=128,swidth=128,noquota 0 1
# /dev/vda1 PARTUUID=a5a6570a-ea64-4480-b6d7-fb3f6a504cbf LABEL=MR_EFI
UUID=C615-2241 /efi vfat rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 0 2
# /dev/vda2 UUID=2a466a62-2a00-4a2e-b8aa-734d71039d67 LABEL=MR_XBOOTLDR
PARTUUID=03b8e7e8-a010-4d06-b09c-5503b609d022 /boot ext4 rw,relatime 0 2
# /dev/dm-4 UUID=fb30403b-2101-4cf9-a083-a5debd8ad60a LABEL=MR_SWAPLV
/dev/dm-4 none swap defaults 0 0
I got this error when trying to chroot:
arch-chroot /mnt
mount: /mnt/dev: udev already mounted on /dev.
dmesg(1) may have more information after failed mount system call.
==> ERROR: failed to setup chroot /mnt
Solution was to:
swapoff -a
umount /mnt/dev
Was irritated bash-completion wasn’t initialized, so set it up for root login (can help diagnoses):
sed -i 's|#if|if|g' $HOME/.bashrc
sed -i 's|#fi|fi|g' $HOME/.bashrc
sed -i 's|# . /etc/bash_completion| . /etc/bash_completion|g' $HOME/.bashrc
# if you also want to uncomment the dircolors aliases:
sed -i 's|#alias|alias|g' $HOME/.bashrc
Make sure snapd
and ufw
are installed (important for post #2):
arch-chroot /mnt apt update && apt list --installed | grep -i -E 'snapd|ufw'
I had them both, but looks like this is when I need to upgrade. Might as well install the kernels now, too, but important to update dpkg
‘s locale
settings before installing any more packages (otherwise, lots of errors):
arch-chroot /mnt
locale-gen "en_US.UTF-8"
dpkg-reconfigure locales
(skip to OK, enter)
(select "en_US.UTF-8", OK, enter)
apt upgrade -y
Install kernel:
cp /usr/lib/kernel/install.conf /etc/kernel
apt install -y linux-image-generic linux-headers-generic
Whilst still in chroot, check and make sure /mnt/boot
has your initrd
with tree
:
apt list --installed | grep linux-image
tree /boot
/boot
├── EFI
│ └── Linux
├── System.map-6.8.0-40-generic
├── $(cat /etc/machine-id)
│ └── 6.8.0-40-generic
│ ├── initrd.img-6.8.0-40-generic
│ └── linux
├── config-6.8.0-40-generic
├── grub
│ ├── gfxblacklist.txt
│ └── unicode.pf2
├── initrd.img -> initrd.img-6.8.0-40-generic
├── initrd.img-6.8.0-40-generic
├── initrd.img.old -> initrd.img-6.8.0-40-generic
├── loader
│ ├── entries
│ │ └── $(cat /etc/machine-id)-6.8.0-40-generic.conf
│ └── entries.srel
├── lost+found
├── vmlinuz -> vmlinuz-6.8.0-40-generic
├── vmlinuz-6.8.0-40-generic
└── vmlinuz.old -> vmlinuz-6.8.0-40-generic
(Still in chroot) install the systemd bootloader – this is best done after linux-image-generic
, since it’ll install GRUB’s BOOTx64.EFI
over systemd-boot’s copy – bootctl update
won’t work for this
bootctl --esp-path=/efi --boot-path=/boot install
Grab efifs
packages (*.efi
files) so systemd-boot
can load your /boot
partition (check the URL https://github.com/pbatard/efifs/releases first to make sure downloading latest ver, and change v1.10
to whatever that might be):
cd /efi/EFI/systemd/ && mkdir drivers && cd drivers
for EFIFS in affs afs bfs btrfs cbfs cpio_be cpio erofs exfat ext2 f2fs fat hfs hfsplus iso9660 jfs minix2_be minix2 minix3_be minix3 minix_be minix newc nilfs2 ntfs odc procfs reiserfs romfs sfs squash4 tar udf ufs1_be ufs1 ufs2 xfs zfs; do \
wget "https://github.com/pbatard/efifs/releases/download/v1.10/${EFIFS}_x64.efi"; done
cd /
Create a user and a root password, give the user sudo
group access:
# root
passwd
# user
useradd -m avery
passwd avery
usermod -aG sudo avery
chsh avery -s /bin/bash
You can check to make sure sudo
is working for your user
su avery
sudo ls /
[sudo] password for avery:
(should output ls /)
If you’re using a constrained space like me, you might want to monitor disk usage so far:
pydf
Filesystem Size Used Avail Use% Mounted on
/dev/mrvg/mrrootlv 16G 2427M 14G 14.9 [#.....] /
/dev/vda2 973M 161M 745M 16.6 [#.....] /boot
/dev/vda1 197M 1756k 195M 0.9 [......] /efi
efivarfs 256k 143k 108k 55.8 [###...] /sys/firmware/efi/efivars
I’m thinking this system should boot now, so I’m going to power off, change the boot device to /dev/vda
, and check it out
exit # chroot
poweroff
TL;DR if you experienced boot failure, perhaps you’d not installed thin-provisioning-tools
before building your initrd
(as I’d neglected). If you did install it, and had no problems, feel free to skip this part unless you’re curious…
I didn’t include thin-provisioning-tools
in one of the earlier lists of modules included with debootstrap
, so my system didn’t boot. There is a file in thin-provisioning-tools
that adds functionality to initramfs-tools
package, namely the hook file /usr/share/initramfs-tools/hooks/thin-provisioning-tools
that adds the dm-thin-pool
module at the bottom of this example of the hook to be compiled along with the rest of the initrd
. Here’s the hook:
# File: /usr/share/initramfs-tools/hooks/thin-provisioning-tools
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
copy_exec /usr/sbin/pdata_tools
ln -s pdata_tools ${DESTDIR}/usr/sbin/cache_check
ln -s pdata_tools ${DESTDIR}/usr/sbin/thin_check
manual_add_modules dm-cache dm-cache-smq dm-thin-pool
If rebuilding your initrd
after installing thin-provisioning-tools
, run update-initramfs -vuk all
and look for lines corresponding to the modules added at the bottom of /usr/share/initramfs-tools/hooks/thin-provisioning-tools
in the output – that should be an indicator that required modules are being added now:
. . .
dracut-install: mkdir '/var/tmp/mkinitramfs_Y5NSco/lib/modules/6.8.0-40-generic/kernel/drivers/md'
dracut-install: cp '/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-cache.ko.zst' '/var/tmp/mkinitramfs_Y5NSco/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-cache.ko.zst'
dracut-install: cp '/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-bufio.ko.zst' '/var/tmp/mkinitramfs_Y5NSco/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-bufio.ko.zst'
dracut-install: cp '/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-bio-prison.ko.zst' '/var/tmp/mkinitramfs_Y5NSco/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-bio-prison.ko.zst'
dracut-install: mkdir '/var/tmp/mkinitramfs_Y5NSco/lib/modules/6.8.0-40-generic/kernel/drivers/md/persistent-data'
dracut-install: cp '/lib/modules/6.8.0-40-generic/kernel/drivers/md/persistent-data/dm-persistent-data.ko.zst' '/var/tmp/mkinitramfs_Y5NSco/lib/modules/6.8.0-40-generic/kernel/drivers/md/persistent-data/dm-persistent-data.ko.zst'
dracut-install: cp '/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-cache-smq.ko.zst' '/var/tmp/mkinitramfs_Y5NSco/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-cache-smq.ko.zst'
dracut-install: cp '/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-thin-pool.ko.zst' '/var/tmp/mkinitramfs_Y5NSco/lib/modules/6.8.0-40-generic/kernel/drivers/md/dm-thin-pool.ko.zst'
. . .
A quick synopsis is that without these tools, initrd will lack required modules, but with it, initrd should boot fine. For a more lengthy explanation, see old workaround:
https://bugs.launchpad.net/ubuntu/+source/lvm2/+bug/1539934/comments/2 [comment 2]
https://askubuntu.com/questions/673815/how-do-i-start-my-laptop-with-root-partition-on-lvm2-thin-pool
Of course, none of this is really necessary if you install the thin-provisioning-tools
package initially.
Last, but definitely not least, set up a skeleton config for snapper
snapshots reference:
systemctl disable snapper-timeline.timer
systemctl enable snapper-boot.timer
systemctl enable snapper-cleanup.timer
snapper --no-dbus -c root create-config \
--fstype="lvm(xfs)" /
snapper --no-dbus set-config \
NUMBER_LIMIT=6 \
NUMBER_LIMIT_IMPORTANT=3 \
TIMELINE_CLEANUP=no \
TIMELINE_CREATE=no \
TIMELINE_LIMIT_DAILY=2 \
TIMELINE_LIMIT_HOURLY=1 \
TIMELINE_LIMIT_WEEKLY=3 \
TIMELINE_LIMIT_MONTHLY=5 \
TIMELINE_LIMIT_QUARTERLY=5 \
TIMELINE_LIMIT_YEARLY=5
snapper --no-dbus list-configs
Next post will be installing maas
to handle dns
and dhcp
on your internet gateway/firewall