Automating creation of Debian VM using Deboostrap
This whole little adventure started as a result of wanting to set up a small VM as a PiHole for ad blocking. There’s no reason that the process had to be automated, but I thought that since I was likely to have to do a bit of messing around with this, it would be good to approach it that way, refresh my memory on kvm/qemu and maybe try out stuff like ansible that I haven’t taken the time to try out before now.
Creating the volume, partition table and filesystems
Going to go for the more “modern” approach, gpt partition table, and using nbd for the mounting step (as opposed to losetup).
The partitioning scheme is 10GB altogether, 270MB EFI partition, followed by 1GB swap (TODO: Does this really make sense??), and remainder as root partition.
qemu-img create -f qcow2 debian-bullseye.qcow2 10G sudo modprobe nbd sudo qemu-nbd -c /dev/nbd0 debian-bullseye.qcow2 sudo parted -s -a optimal -- /dev/nbd0 mklabel gpt \ mkpart primary fat32 1MiB 270MiB \ mkpart primary linux-swap 300MiB 1GiB \ mkpart primary ext4 1GiB -0 \ name 1 uefi \ name 2 swap \ name 3 root \ set 1 esp on sudo mkfs -t fat -F 32 -n EFI /dev/nbd0p1 sudo mkswap -L swap /dev/nbd0p2 sudo mkfs -t ext4 -L root /dev/nbd0p3 sudo blkid # Need to do sudo so that we refresh the records swap_uuid="$(/usr/sbin/blkid | grep '^/dev/nbd0' | \ grep ' LABEL="swap" ' | grep -o ' UUID="[^"]\+"' | sed -e 's/^ //' )" root_uuid="$(/usr/sbin/blkid | grep '^/dev/nbd0' | \ grep ' LABEL="root" ' | grep -o ' UUID="[^"]\+"' | sed -e 's/^ //' )" efi_uuid="$(/usr/sbin/blkid | grep '^/dev/nbd0' | \ grep ' LABEL="EFI" ' | grep -o ' UUID="[^"]\+"' | sed -e 's/^ //' )" sudo mount $root_uuid /mnt/tmp/
Initial mount and running deboostrap
I’m going to mount and build the system under /mnt/tmp, that may or may not suit for your circumstances. Additionally, the packages to include will likely need a bit of tweaking depending on the purposes you have in mind for the VM after build.
For what it’s worth, the deboostrap takes about 9 minutes on my machine/network connection. Note the inclusion of the kernel packages and grub which are of course required to make the system bootable. I’ve included grub-pc-bin as well as the grub-efi-amd64 package that is expected to be used primarily.
sudo mount $root_uuid /mnt/tmp/ sudo /sbin/debootstrap --arch amd64 \ --include=rsync,vim,sudo,openssh-server,\ linux-image-amd64,grub-pc-bin,grub-efi-amd64\ bullseye /mnt/tmp http://ftp.uk.debian.org/debian
Need an /etc/fstab file to be created too. Blkid being used as it’s more robust/unambiguous than most other approaches…
cat - >> ./tmp.fstab << EOF # /etc/fstab: static file system information. # # Use 'blkid' to print the universally unique identifier for a # device; this may be used with UUID= as a more robust way to name devices # that works even if disks are added and removed. See fstab(5). # # <file system> <mount point> <type> <options> <dump> <pass> $swap_uuid none swap sw 0 0 $root_uuid / ext4 errors=remount-ro 0 1 $efi_uuid /boot/efi vfat defaults 0 1 EOF sudo mv ./tmp.fstab /mnt/tmp/etc/fstab
Chroot into the new system
sudo mount -o bind,ro /dev /mnt/tmp/dev sudo mount -t proc none /mnt/tmp/proc sudo mount -t sysfs none /mnt/tmp/sys sudo LANG=C.UTF-8 chroot /mnt/tmp /bin/bash
And then commands within the chroot environment (will wrap these up into a script too):
debconf-set-selections <<EOF tzdata tzdata/Areas select Europe tzdata tzdata/Zones/Europe select London EOF # This is necessary as tzdata will assume these are manually set and override the debconf values with their settings rm -f /etc/localtime /etc/timezone DEBCONF_NONINTERACTIVE_SEEN=true dpkg-reconfigure -f noninteractive tzdata cat - >> /etc/network/interfaces << EOF auto lo iface lo inet loopback EOF echo "debian-bullseye" > /etc/hostname echo "127.0.1.1 debian-bullseye.cryptid.icu debian-bullseye" >> /etc/hosts cat - >> /etc/apt/sources.list << EOF # First should already be there # deb http://ftp.uk.debian.org/debian/ bullseye main deb http://deb.debian.org/debian/ bullseye main contrib non-free deb http://deb.debian.org/debian/ bullseye-updates main contrib non-free deb-src http://deb.debian.org/debian/ bullseye main contrib non-free EOF apt update debconf-set-selections <<EOF locales locales/locales_to_be_generated multiselect en_GB.UTF-8 UTF-8 locales locales/default_environment_locale select en_GB.UTF-8 keyboard-configuration keyboard-configuration/layoutcode string gb keyboard-configuration keyboard-configuration/variant select English (UK) keyboard-configuration keyboard-configuration/model select Generic 105-key PC (intl.) EOF # Stop anything overriding debconf's settings rm -f /etc/default/locale /etc/locale.gen /etc/default/keyboard apt-get install locales console-setup
This was probably what I found trickiest first time through, or made the most mistakes with. Remember, this is still happening within the chroot environment! I’m lifting wholesale here from Laurence Hurst.
apt-get install grub-efi-amd64 # Add console=ttyS0 so we get early boot messages on the serial console. sed -i -e 's/^\\(GRUB_CMDLINE_LINUX="[^"]*\\)"$/\\1 console=ttyS0"/' /etc/default/grub # Tell GRUB to use the serial console cat - >>/etc/default/grub <<EOF GRUB_TERMINAL="serial" GRUB_SERIAL_COMMAND="serial --unit=0 --speed=9600 --stop=1" EOF grub-install --target=x86_64-efi update-grub # Now exit the chroot umount /boot/efi/ exit
Tidying up and try first boot…
sudo umount /mnt/tmp/dev /mnt/tmp/proc /mnt/tmp/sys /mnt/tmp/boot/efi sudo umount /mnt/tmp # and detach the volume sudo qemu-nbd -d /dev/nbd0
Collecting some of the current pages I have open, and which I read on route to solution.
- quite detailed and through, also has a github. Overcomplicated for my purposes perhaps (e.g. use of efi for boot). Uses nbd though which I hadn’t been familiar with up to now (vs loopback device).
- decent description. Use of sfdisk a bit unorthodox perhaps, and the command line options supplied don’t actually work (can be fixed / made to work… but why not just use e.g. parted?)
- simple description. Builds off previous link from Diogo Gomes.
- This was actually the first link that worked for me (had more problems overall than I’d expected). Uses losetup (which was what I was more familiar with), raw file (not qcow2),