Notes on VM building: Difference between revisions

From FnordWiki
Jump to navigation Jump to search
 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
What is says in the title.
What is says in the title.


== Let's build a modern VM ==
Here's what I run to start a Debian install with a virtio storage and network enabled UEFI virtual machine:
Here's what I run to start a Debian install with a virtio storage and network enabled UEFI virtual machine:
$ '''sudo qemu-img create -f qcow2 /srv/vm-backing-store/padmeamadeus-0-vda.qcow2 64G'''
$ '''sudo qemu-img create -f qcow2 /srv/vm-backing-store/padmeamadeus-0-vda.qcow2 64G'''
Line 205: Line 206:


[https://openzfs.github.io/openzfs-docs/Getting%20Started/Debian/Debian%20Bookworm%20Root%20on%20ZFS.html Debian Bookworm Root on ZFS] is my standard reference material.
[https://openzfs.github.io/openzfs-docs/Getting%20Started/Debian/Debian%20Bookworm%20Root%20on%20ZFS.html Debian Bookworm Root on ZFS] is my standard reference material.

Some things to do first, though:
# Create a new qcow2 file for the VM. See top of this document for an example.
# Add a second disk drive to the VM. <code>virsh</code>'s <code>edit</code> command to edit the VM's XML config file. Attach to a new PCI bus instance. Change name from <code>vda</code> to <code>vdb</code>, too.
# Restart (not just reboot -- qemu needs to stop to get the new VM config XML file) the VM.
# In the VM, install:
## parted
## cryptsetup
## dosfstools
# And in the VM, partition the newly attached disk. We need a GUID partition table (GPT) because we're running UEFI firmare. I like parted for this, but use whatever tool works for you. Here's a <code>parted</code> session transcript:
$ '''sudo parted /dev/vdb '''
GNU Parted 3.6
Using /dev/vdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) '''unit s'''
(parted) '''mktable gpt'''
(parted) '''mkpart EFI_boot 2048 2099199'''
(parted) '''mkpart bpool 2099200 6293503'''
(parted) '''mkpart data 6293504 134217693'''
(parted) '''set 1 esp on'''
(parted) '''p'''
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 134217728s
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 2048s 2099199s 2097152s EFI_boot boot, esp
2 2099200s 6293503s 4194304s bpool
3 6293504s 134217693s 127924190s data
(parted) '''quit'''
Information: You may need to update /etc/fstab.
$
Now, partition 3 of the new disk will get the LUKS encryption goodness:
$ '''sudo cryptsetup luksFormat --verbose --hash sha512 --iter-time 30000 --key-size 512 --pbkdf argon2id --pbkdf-memory $((4 * 1024 * 1024)) /dev/vdb3'''
WARNING!
========
This will overwrite data on /dev/vdb3 irrevocably.
Are you sure? (Type 'yes' in capital letters): '''YES'''
Enter passphrase for /dev/vdb3:
Verify passphrase:
Key slot 0 created.
Command successful.
$
From here, follow the instructions in the ZFS on Linux [https://openzfs.github.io/openzfs-docs/Getting%20Started/Debian/Debian%20Bookworm%20Root%20on%20ZFS.html Debian Bookworm Root on ZFS] wiki page.
== Additional ZFS related notes ==
zpool creation commands:
$ '''sudo zpool create -o ashift=12 -o autotrim=on -O acltype=posixacl -O xattr=sa -O dnodesize=auto -O compression=lz4 -O normalization=formD -O relatime=on -O canmount=off -O mountpoint=/ -R /mnt rpool /dev/mapper/vdb3_plain'''
$ '''sudo zpool create -o ashift=12 -o autotrim=on -o compatibility=grub2 -O devices=off -O acltype=posixacl -O xattr=sa -O compression=lz4 -O normalization=formD -O relatime=on -O canmount=off -O mountpoint=/boot -R /mnt bpool /dev/vdb2'''

ZFS filesystems created as per the [https://openzfs.github.io/openzfs-docs/Getting%20Started/Debian/Debian%20Bookworm%20Root%20on%20ZFS.html#step-2-disk-formatting Debian Bookworm Root on ZFS] instructions:
$ '''sudo zfs create -o canmount=off -o mountpoint=none rpool/ROOT'''
$ '''sudo zfs create -o canmount=off -o mountpoint=none bpool/BOOT'''
$ '''sudo zfs create -o canmount=noauto -o mountpoint=/ rpool/ROOT/debian'''
$ '''sudo zfs create -o mountpoint=/boot bpool/BOOT/debian'''
$ '''sudo zfs mount rpool/ROOT/debian'''
$ '''sudo zfs create rpool/home'''
$ '''sudo zfs create -o mountpoint=/root rpool/home/root'''
$ '''sudo chmod 700 /mnt/root'''
$ '''sudo zfs create -o canmount=off rpool/var'''
$ '''sudo zfs create -o canmount=off rpool/var/lib'''
$ '''sudo zfs create rpool/var/log'''
$ '''sudo zfs create rpool/var/spool'''
$ '''sudo zfs create -o com.sun:auto-snapshot=false rpool/var/cache'''
$ '''sudo zfs create -o com.sun:auto-snapshot=false rpool/var/lib/nfs'''
$ '''sudo zfs create -o com.sun:auto-snapshot=false rpool/var/tmp'''
$ '''sudo chmod 1777 /mnt/var/tmp'''
$ '''sudo zfs create rpool/srv'''
$ '''sudo zfs create rpool/var/www'''
$ '''sudo zfs create -o com.sun:auto-snapshot=false rpool/tmp'''
$ '''sudo chmod 1777 /mnt/tmp'''
$ '''sudo zfs create rpool/opt'''

== And some debootstrapping goodness ==
$ '''sudo debootstrap --variant=minbase --components=main,contrib,non-free,non-free-firmware \'''
'''--include=apt,bash-completion,cryptsetup-initramfs,dhcpcd-base,dkms,dosfstools,grub-efi-amd64-signed,ifupdown,iproute2,less,linux-headers-6.12.38+deb13-amd64,linux-image-6.12.38+deb13-amd64,man-db,mandos-client,ntpsec,openssh-client,openssh-server,rsyslog,screen,shim-signed,sudo,systemd,systemd-sysv,vim-nox,whiptail,zfs-dkms,zfs-initramfs \'''
'''trixie /mnt'''

Latest revision as of 14:14, 31 July 2025

What is says in the title.

Let's build a modern VM

Here's what I run to start a Debian install with a virtio storage and network enabled UEFI virtual machine:

$ sudo qemu-img create -f qcow2 /srv/vm-backing-store/padmeamadeus-0-vda.qcow2 64G
$ sudo virt-install --name padmeamadeus-0 --memory 5120 --vcpus 2 --cpu host --cdrom ~/Downloads/debian-testing-amd64-20250726-netinst.iso \
      --boot uefi --osinfo debian11 --disk /srv/vm-backing-store/padmeamadeus-0-vda.qcow2,bus=virtio --network bridge=br0,model=virtio \
      --graphics=spice

5 Gbytes of RAM provisioned. The VM's disk will (eventually) be encrypted with LUKS and 4Gibytes of RAM will be needed for the disk encryption passphrase operation (argon2id) that unlocks the data.

Now that there is a UEFI VM created, it is time to build some ZFS DKMS modules. Firstly, what kernel are we running?

$ dpkg -l linux-image-\* | grep ^ii
ii  linux-image-6.12.38+deb13-amd64          6.12.38-1    amd64        Linux 6.12 for 64-bit PCs (signed)
ii  linux-image-amd64                        6.12.38-1    amd64        Linux for 64-bit PCs (meta-package)
$

Next up, we need to install the matching kernel headers package, the DKMS package, and the ZFS DKMS source package:

$ sudo apt-get install linux-headers-6.12.38+deb13-amd64 dkms zfs-dkms
[lots of output elided]
$

And try to see about getting the ZFS code inserted into the running kernel:

$ sudo modprobe zfs
modprobe: ERROR: could not insert 'zfs': Key was rejected by service
$

Well, that's irritating. The interwebs say this is related to secure boot. And normally, I'd fix that by just turning secure boot off. But maybe the time has come to learn how to deal with it?

Secure boot blah blah blah

OK, so secure boot requires cryptographically signed code from the boot loader (GRUB and its components) to the kernel. And runtime loadable kernel modules need some signing, too. At least with Linux. Maybe not so much with other OSes? It looks like maybe DKMS did actually sign the module, but the VM does not know about the key used:

$ /sbin/modinfo zfs
filename:       /lib/modules/6.12.38+deb13-amd64/updates/dkms/zfs.ko.xz
version:        2.3.2-2
license:        CDDL
license:        Dual BSD/GPL
license:        Dual MIT/GPL
author:         OpenZFS
description:    ZFS
alias:          zzstd
alias:          zcommon
alias:          zunicode
alias:          znvpair
alias:          zlua
alias:          icp
alias:          zavl
alias:          devname:zfs
alias:          char-major-10-249
srcversion:     A955C79D4F11DB309BB8F3C
depends:        spl
name:           zfs
retpoline:      Y
vermagic:       6.12.38+deb13-amd64 SMP preempt mod_unload modversions 
sig_id:         PKCS#7
signer:         DKMS module signing key
sig_key:        7C:77:72:2A:2C:03:2E:02:3D:9F:FE:76:83:7F:20:F5:1E:79:0C:F0
sig_hashalgo:   sha256
signature:      3B:77:18:7B:AC:B2:C8:8F:E5:E4:05:CA:3B:09:19:CD:4C:27:6D:98:
		35:33:11:F1:58:D1:73:70:B4:CE:52:1A:9D:59:E2:5C:D2:5E:A6:65:
		EF:88:6C:4E:3B:13:33:50:FC:48:BA:1A:5F:FB:62:51:4B:6C:39:56:
		3B:EC:DD:9F:18:5B:6D:AC:6C:43:4A:FF:97:3D:5C:EC:83:13:CE:7D:
		AF:E6:70:9C:6D:4E:E7:00:3F:F9:53:51:87:B7:99:95:A1:A7:74:D6:
		8A:B7:4A:55:3F:4E:57:08:99:2B:8A:6B:1A:DA:1D:33:5C:3E:E2:37:
		00:42:BA:9D:81:AE:7D:12:BC:75:64:55:B4:75:F9:F5:3D:90:4C:E5:
		AB:5D:FF:98:A2:B1:0C:C5:0F:D5:5D:D2:73:78:21:78:CD:74:C4:34:
		35:32:C9:4B:E5:3A:6F:CE:B5:75:B0:F0:56:4F:49:B3:71:CB:9A:48:
		91:D2:D2:9E:19:AF:3B:68:87:42:F7:93:5A:E3:73:53:48:20:64:37:
		0D:33:CD:29:1A:73:B4:77:26:6D:B9:E4:54:46:F9:ED:50:BA:DA:DB:
		42:6F:D3:9E:19:86:A1:75:01:55:9F:A6:77:CA:58:1F:C6:2F:BC:02:
		B0:1F:16:2D:2B:21:39:47:DB:60:B7:02:A3:7A:5F:0C
[lots of info about ZFS code tunables removed for brevity]
$

So, yeah. The locally built ZFS module was signed by DKMS module signing key. How do we tell the VM that key is allowed?

Machine Owner Keys

A secure boot enabled computer's owner can install new keys for code signing. Reading the documentation at Debian's Secure Boot wiki page, it would appear I missed a step. Should have generated a local DKMS Machine Owner Key before installing the zfs-dkms package. Following instructions found there, let's try again:

$ sudo dkms generate_mok
Signing key: /var/lib/dkms/mok.key
Public certificate (MOK): /var/lib/dkms/mok.pub
$ sudo mokutil --import /var/lib/dkms/mok.pub
input password: 
input password again: 
itops@padmeamadeus-0:~$ sudo mokutil --list-new
[key 1]
Owner: 605dab50-e046-4300-abb6-3dd810dd8b23
SHA1 Fingerprint: de:a6:98:bb:b9:db:5f:f5:6b:ff:8d:18:b8:3f:30:82:54:ee:9c:61
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            7c:77:72:2a:2c:03:2e:02:3d:9f:fe:76:83:7f:20:f5:1e:79:0c:f0
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=DKMS module signing key
        Validity
            Not Before: Jul 26 17:53:44 2025 GMT
            Not After : Jul  2 17:53:44 2125 GMT
        Subject: CN=DKMS module signing key
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:e0:30:8f:9a:46:c2:1e:47:26:d7:02:d6:d0:d6:
                    f5:10:14:2d:db:64:78:63:c0:58:65:a8:23:c6:4f:
                    d1:92:2a:49:53:0c:a3:72:38:5a:a4:50:ee:15:ad:
                    73:b6:92:d6:25:30:6c:bc:80:7f:24:1b:36:6b:c0:
                    e1:ad:14:b2:cb:ea:42:0a:01:bd:db:92:7d:45:66:
                    6f:2a:70:b8:ea:cb:4c:eb:d3:cb:a0:cf:b2:2a:3d:
                    3e:f6:56:2b:d4:7d:04:e0:12:93:fe:f2:d4:71:ed:
                    33:f5:ad:58:d6:be:d9:e4:05:ea:38:9f:ce:ef:b5:
                    fa:76:70:c0:09:28:b9:32:2d:f8:40:fa:c5:69:9b:
                    e8:43:9c:29:44:83:54:6c:32:ee:a3:8e:6a:3c:68:
                    bb:8b:34:ff:29:76:43:f7:df:81:45:5f:2a:7f:40:
                    fb:69:8e:6b:4d:8d:c3:60:84:15:f9:a3:2e:66:1d:
                    4c:ac:7d:41:5b:d2:47:47:bc:80:17:63:42:63:96:
                    f9:5e:29:c4:f4:18:f1:3b:82:be:5e:1b:97:f9:1c:
                    74:ba:83:fe:41:3f:ed:b9:45:5c:66:af:75:35:04:
                    90:c7:50:cc:cd:33:0b:fc:60:99:c6:c3:69:b4:22:
                    18:7b:f2:6b:66:83:3d:d8:50:cb:6e:93:02:2f:52:
                    8c:c9
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                DB:36:87:4C:32:A4:78:50:0D:27:4D:21:9B:80:59:80:BC:41:10:42
            X509v3 Authority Key Identifier: 
                DB:36:87:4C:32:A4:78:50:0D:27:4D:21:9B:80:59:80:BC:41:10:42
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Extended Key Usage: 
                Code Signing
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        70:b7:c9:74:18:9f:59:1b:ab:2e:fc:2f:8a:cb:ae:75:ef:3e:
        59:a2:e1:40:6c:25:90:65:93:4f:94:07:37:17:fb:99:0b:84:
        8e:7a:2b:b2:28:d8:9a:db:68:f8:8f:f6:6e:a5:b7:1c:36:9a:
        16:53:b7:47:ee:2a:fa:23:5c:cf:f3:6a:57:28:0e:dd:69:42:
        d1:a8:c4:47:5f:2d:cd:22:a1:b0:fd:3f:10:28:4a:72:13:4f:
        8f:ef:82:95:46:83:af:ac:16:a4:59:a3:dd:52:b1:dd:b5:64:
        53:28:74:45:95:d2:ff:08:e4:98:fa:89:ad:df:2e:5d:76:d9:
        73:9f:8e:57:b6:14:c8:28:0f:58:68:f1:62:e1:ca:4f:b1:86:
        11:1a:20:d2:ed:82:15:07:33:b4:94:40:03:4f:18:8b:b1:10:
        1b:11:3d:0d:8d:30:3c:03:b0:d0:95:0b:b0:0a:b5:31:60:37:
        2b:42:ad:0e:6e:f7:e8:22:81:95:1c:a4:04:39:ce:54:ba:dd:
        7c:31:c1:2f:86:78:3d:59:df:1f:98:06:a6:99:e9:ac:99:5e:
        57:2e:fd:71:ef:2f:c4:fb:da:78:09:28:09:b9:f4:d2:88:ec:
        56:76:c5:38:91:d6:ad:ba:2a:f3:85:b4:c3:e8:9d:89:0d:75:
        26:c8:ea:27
$ sudo shutdown -r now

The VM will reboot and the secure boot shim part of GRUB will ask for the password to enroll the key in the UEFI's list of machine owner keys. Be prepared to interact with the console. The process is menu driven, but if the grub timeout passes, it will boot automatically, skipping the key enrollment step.

After enrolling the new machine owner key (MOK) from the bootloader, the system will reboot again. And when that is done, we can see that it now has the just-created DKMS MOK enrolled in a couple of ways:

$ sudo dmesg -T | grep cert
[Sun Jul 27 17:02:59 2025] Loading compiled-in X.509 certificates
[Sun Jul 27 17:02:59 2025] Loaded X.509 cert 'Build time autogenerated kernel key: ba77a71e64c453bc15410391734927a088a45279'
[Sun Jul 27 17:02:59 2025] integrity: Loading X.509 certificate: UEFI:db
[Sun Jul 27 17:02:59 2025] integrity: Loaded X.509 cert 'Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e4f9ae17c55af53'
[Sun Jul 27 17:02:59 2025] integrity: Loading X.509 certificate: UEFI:db
[Sun Jul 27 17:02:59 2025] integrity: Loaded X.509 cert 'Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed522988a1bd4'
[Sun Jul 27 17:02:59 2025] integrity: Loading X.509 certificate: UEFI:MokListRT (MOKvar table)
[Sun Jul 27 17:02:59 2025] integrity: Loaded X.509 cert 'Debian Secure Boot CA: 6ccece7e4c6c0d1f6149f3dd27dfcc5cbb419ea1'
[Sun Jul 27 17:02:59 2025] integrity: Loading X.509 certificate: UEFI:MokListRT (MOKvar table)
[Sun Jul 27 17:02:59 2025] integrity: Loaded X.509 cert 'DKMS module signing key: db36874c32a478500d274d219b805980bc411042'
[Sun Jul 27 17:03:05 2025] cfg80211: Loading compiled-in X.509 certificates for regulatory database
[Sun Jul 27 17:03:05 2025] Loaded X.509 cert 'benh@debian.org: 577e021cb980e0e820821ba7b54b4961b8b4fadf'
[Sun Jul 27 17:03:05 2025] Loaded X.509 cert 'romain.perier@gmail.com: 3abbc6ec146e09d1b6016ab9d6cf71dd233f0328'
[Sun Jul 27 17:03:05 2025] Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
[Sun Jul 27 17:03:05 2025] Loaded X.509 cert 'wens: 61c038651aabdcf94bd0ac7ff06c7248db18c600'
$ sudo mokutil --list-enrolled | grep -e ^.key\  -e Subject:
[key 1]
        Subject: CN=Debian Secure Boot CA
[key 2]
        Subject: CN=DKMS module signing key
$

Rebuild the ZFS modules (now signed by an enrolled MOK)

This should be pretty simple:

$ sudo dpkg-reconfigure zfs-dkms
Module zfs/2.3.2 for kernel 6.12.38+deb13-amd64 (x86_64):
Before uninstall, this module version was ACTIVE on this kernel.
Deleting /lib/modules/6.12.38+deb13-amd64/updates/dkms/zfs.ko.xz
Deleting /lib/modules/6.12.38+deb13-amd64/updates/dkms/spl.ko.xz
Running depmod..... done.

Deleting module zfs/2.3.2 completely from the DKMS tree.
Loading new zfs/2.3.2 DKMS files...
Building for 6.12.38+deb13-amd64

Building initial module zfs/2.3.2 for 6.12.38+deb13-amd64
Sign command: /lib/modules/6.12.38+deb13-amd64/build/scripts/sign-file
Signing key: /var/lib/dkms/mok.key
Public certificate (MOK): /var/lib/dkms/mok.pub

Running the pre_build script............................................. done.
Building module(s)................................................................... done.
Signing module /var/lib/dkms/zfs/2.3.2/build/module/zfs.ko
Signing module /var/lib/dkms/zfs/2.3.2/build/module/spl.ko
Running the post_build script... done.
Installing /lib/modules/6.12.38+deb13-amd64/updates/dkms/zfs.ko.xz
Installing /lib/modules/6.12.38+deb13-amd64/updates/dkms/spl.ko.xz
Running depmod..... done.
$

And now, we can install the ZFS modules without error:

$ sudo modprobe -v zfs
$ lsmod | grep zfs
zfs                  6676480  6
spl                   159744  1 zfs
$

Great success!

And now for some ZFS goodness

I've done this a number of times already on different systems. Including the firewall machines, the main server system, the backup server, and the GNU source repo mirror VM. So the notes here are pretty spare.

Debian Bookworm Root on ZFS is my standard reference material.

Some things to do first, though:

  1. Create a new qcow2 file for the VM. See top of this document for an example.
  2. Add a second disk drive to the VM. virsh's edit command to edit the VM's XML config file. Attach to a new PCI bus instance. Change name from vda to vdb, too.
  3. Restart (not just reboot -- qemu needs to stop to get the new VM config XML file) the VM.
  4. In the VM, install:
    1. parted
    2. cryptsetup
    3. dosfstools
  5. And in the VM, partition the newly attached disk. We need a GUID partition table (GPT) because we're running UEFI firmare. I like parted for this, but use whatever tool works for you. Here's a parted session transcript:
$ sudo parted /dev/vdb 
GNU Parted 3.6
Using /dev/vdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit s
(parted) mktable gpt
(parted) mkpart EFI_boot 2048 2099199
(parted) mkpart bpool 2099200 6293503
(parted) mkpart data 6293504 134217693
(parted) set 1 esp on
(parted) p
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 134217728s
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start     End         Size        File system  Name      Flags
 1      2048s     2099199s    2097152s                 EFI_boot  boot, esp
 2      2099200s  6293503s    4194304s                 bpool
 3      6293504s  134217693s  127924190s               data

(parted) quit

Information: You may need to update /etc/fstab.

$

Now, partition 3 of the new disk will get the LUKS encryption goodness:

$ sudo cryptsetup luksFormat --verbose --hash sha512 --iter-time 30000 --key-size 512 --pbkdf argon2id --pbkdf-memory $((4 * 1024 * 1024)) /dev/vdb3

WARNING!
========
This will overwrite data on /dev/vdb3 irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/vdb3: 
Verify passphrase: 
Key slot 0 created.
Command successful.
$

From here, follow the instructions in the ZFS on Linux Debian Bookworm Root on ZFS wiki page.

Additional ZFS related notes

zpool creation commands:

$ sudo zpool create -o ashift=12 -o autotrim=on -O acltype=posixacl -O xattr=sa -O dnodesize=auto -O compression=lz4 -O normalization=formD -O relatime=on -O canmount=off -O mountpoint=/ -R /mnt rpool /dev/mapper/vdb3_plain
$ sudo zpool create -o ashift=12 -o autotrim=on -o compatibility=grub2 -O devices=off -O acltype=posixacl -O xattr=sa -O compression=lz4 -O normalization=formD -O relatime=on -O canmount=off -O mountpoint=/boot -R /mnt bpool /dev/vdb2

ZFS filesystems created as per the Debian Bookworm Root on ZFS instructions:

$ sudo zfs create -o canmount=off -o mountpoint=none rpool/ROOT
$ sudo zfs create -o canmount=off -o mountpoint=none bpool/BOOT
$ sudo zfs create -o canmount=noauto -o mountpoint=/ rpool/ROOT/debian
$ sudo zfs create -o mountpoint=/boot bpool/BOOT/debian
$ sudo zfs mount rpool/ROOT/debian
$ sudo zfs create rpool/home
$ sudo zfs create -o mountpoint=/root rpool/home/root
$ sudo chmod 700 /mnt/root
$ sudo zfs create -o canmount=off rpool/var
$ sudo zfs create -o canmount=off rpool/var/lib
$ sudo zfs create rpool/var/log
$ sudo zfs create rpool/var/spool
$ sudo zfs create -o com.sun:auto-snapshot=false rpool/var/cache
$ sudo zfs create -o com.sun:auto-snapshot=false rpool/var/lib/nfs
$ sudo zfs create -o com.sun:auto-snapshot=false rpool/var/tmp
$ sudo chmod 1777 /mnt/var/tmp
$ sudo zfs create rpool/srv
$ sudo zfs create rpool/var/www
$ sudo zfs create -o com.sun:auto-snapshot=false  rpool/tmp
$ sudo chmod 1777 /mnt/tmp
$ sudo zfs create rpool/opt

And some debootstrapping goodness

$ sudo debootstrap --variant=minbase --components=main,contrib,non-free,non-free-firmware \
    --include=apt,bash-completion,cryptsetup-initramfs,dhcpcd-base,dkms,dosfstools,grub-efi-amd64-signed,ifupdown,iproute2,less,linux-headers-6.12.38+deb13-amd64,linux-image-6.12.38+deb13-amd64,man-db,mandos-client,ntpsec,openssh-client,openssh-server,rsyslog,screen,shim-signed,sudo,systemd,systemd-sysv,vim-nox,whiptail,zfs-dkms,zfs-initramfs \
    trixie /mnt