Install Debian Bookworm with encrypted Root-on-ZFS
![Debian logo](/img/debian-ascii2.png)
Debian 12 aka "Bookworm" is the latest stable release of the popular Linux operating system. I use Debian's live install image to create an encrypted, minimal, console-only base configuration using the OpenZFS filesystem that can be customized for various types of servers and desktops.
This is how I do it...
1. Let's go!
Debian GNU/Linux is an operating system created by volunteers of one of the largest and longest-running free software projects in the world. There are three release branches: stable (code-named Bookworm), testing (Trixie), and unstable (Sid).
Below is a walk-through of an installation that makes use of the entire disk divided into three partitions: an ESP
system partition (UEFI systems), an encrypted swap
device, and an encrypted Root-on-ZFS root
partition.
1.1 Setup
- Target device is
x86_64
architecture - Debian is the sole OS on a single disk (example: sda)
- UEFI boot using ZFSBootMenu as bootloader
- ZFS native encryption is enabled
- GPT partition table with 3 partitions:
- sda1
- Mount:
/boot/efi
; Size:1GB
; Format:vfat
; Use as:EFI system partition
- Mount:
- sda2
- Mount:
(swap)
; Size:(Device RAM * 2)
; Format:luks
; Use as:encrypted partition
- Device:
/dev/mapper/swap
; Format:swap
; Use as:swap device
- Mount:
- sda3
- Mount:
/
; Size:->END
; Format:zfs
; Use as:encrypted ZFS pool
- Mount:
- sda1
1.2 Download
Download debian-live-12.6.0-amd64-standard.iso and SHA256SUMS.
Verify image integrity:
$ sha256sum -c --ignore-missing SHA256SUMS
debian-live-12.6.0-amd64-standard.iso: OK
1.3 Prepare install media
Prepare a USB storage drive as an installer using one of these two methods:
Method 1: Ventoy
I now use Ventoy to setup a USB device to be a multiboot installer. Simply copy an iso to the device, reboot, and the auto-generated menu lists all the disk images available to boot. Read more
Method 2: dd
Write the installer to an unmounted USB storage device using the dd
command as root.
BE VERY CAREFUL TO NOTE THE PROPER DEVICE. ALL DATA ON THE DEVICE WILL BE OVERWRITTEN.
Example: On a Linux system, if a USB stick appears as sdx1
, then write the installer to sdx
(no partition number):
dd if=/path/to/debian-live-12.4.0-amd64-standard.iso of=/dev/sdX bs=1M status=progress oflag=sync
sync
1.4 Boot
Press Enter
to boot the first menu item: Live system (amd64)
debian login: user (automatic login)
...
user@debian:~$
Switch to a root shell:
sudo -i
Confirm EFI support:
# dmesg | grep -i efivars
[ 0.301784] Registered efivars operations
2. Configure live environment
2.1 Network
By default, wired (ethernet) interfaces are configured for auto-detection and to use DHCP.
Display all detected network interfaces along with their IP and MAC addresses:
ip addr
Debian's network interfaces are configured in /etc/network/interfaces
and controlled by the ifup
and ifdown
commands.
Example entry for wired:
allow-hotplug enp0s31f6
iface enp0s31f6 inet dhcp
For wireless:
allow-hotplug wlp61s0
iface wlp61s0 inet dhcp
wpa-ssid <wifi_access_point_name>
wpa-psk <wifi_passphrase>
2.2 Optional: Continue install from another computer via SSH
This makes the install easier by cut-and-pasting these commands into a terminal:
apt update && apt install -y openssh-server
SSH server is auto-launched after install.
Assign password to user
:
passwd user
Switch to the other computer and SSH into the target device:
ssh user@<ip_address>
Switch to root:
sudo -i
2.3 Keymap
Default console keymap is US QWERTY
.
For a different keymap, install:
apt install -y console-data
Available keymaps are listed in /usr/share/keymaps/
.
Select a different keymap by running:
dpkg-reconfigure keyboard-configuration
setupcon
2.4 Console font
Discover available fonts in /usr/share/consolefonts
.
Default font in the installer might prove too small on high resolution displays. Set and display a larger font (example: Lat15-TerminusBold22x11
) for the current session:
setfont Lat15-TerminusBold22x11
2.5 Source /etc/os-release
File /etc/os-release
defines variables that describe the current operating system. Use the $ID
variable to set the short name of the distribution in later commands:
source /etc/os-release
export ID
2.6 Configure package manager
# cat <<EOF > /etc/apt/sources.list
> deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
> deb-src http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
> deb http://deb.debian.org/debian-security/ bookworm-security main contrib non-free non-free-firmware
> EOF
Update package database:
apt update
2.7 Install tools
apt install -y debootstrap gdisk dkms linux-headers-$(uname -r) parted
apt install -y zfsutils-linux zfs-dkms zfs-zed
Verify module:
modprobe zfs && zfs version
2.8 Generate /etc/hostid
zgenhostid -f
Link: zgenhostid.8
2.9 Define disk variables
Identify the internal storage device where Debian will be installed by listing block devices:
lsblk
Set disk variables for either a SATA or NVME disk:
SATA DISK (example: sda)
export DISK="/dev/sda"
export EFI_PART="1"
export SWAP_PART="2"
export POOL_PART="3"
export EFI_DISK="${DISK}${EFI_PART}"
export SWAP_DISK="${DISK}${SWAP_PART}"
export POOL_DISK="${DISK}${POOL_PART}"
NVME DISK (example: nvme0n1)
export DISK="/dev/nvme0n1"
export EFI_PART="1"
export SWAP_PART="2"
export POOL_PART="3"
export EFI_DISK="${DISK}p${EFI_PART}"
export SWAP_DISK="${DISK}p${SWAP_PART}"
export POOL_DISK="${DISK}p${POOL_PART}"
3. Partition disk
3.1 Wipe old partitions
wipefs -af $DISK
sgdisk --zap-all $DISK
partprobe $DISK
3.2 Create new partitions
List partition type codes:
sgdisk --list-types
Create EFI partition:
sgdisk -n "${EFI_PART}:1m:+1g" -t "${EFI_PART}:ef00" -c 0:esp $DISK
Create swap partition:
sgdisk -n "${SWAP_PART}:0:+16g" -t "${SWAP_PART}:8200" -c 0:swap $DISK
Create zpool partition (remaining space):
sgdisk -n "${POOL_PART}:0:-10m" -t "${POOL_PART}:bf00" -c 0:pool $DISK
Display layout:
partprobe $DISK && sgdisk -p $DISK
4. ZFS pool creation
When adding disks or partitions to ZFS pools, its important to use their symbolic links created in /dev/disk/by-id
or (on UEFI systems) /dev/disk/by-partuuid
. This will ensure that ZFS identifies the correct device even if disk naming should change at some point. Using traditional device nodes like /dev/sda3
may cause import failures.
4.1 Define POOL_ID variable
Define variable to hold the PARTUUID
of $POOL_DISK
:
export POOL_ID=/dev/disk/by-partuuid/$( blkid | grep "${POOL_DISK}" | awk -F "=" '{print $NF}' | cut -d '"' -f 2 )
4.2 Store pool passphrase in key file
echo -n 'SomeLongKeyphrase' > /etc/zfs/zroot.key
chmod 000 /etc/zfs/zroot.key
4.3 Setup encrypted zpool
# zpool create -f -o ashift=12 \
-O compression=lz4 \
-O acltype=posixacl \
-O xattr=sa \
-O relatime=on \
-o autotrim=on \
-O encryption=aes-256-gcm \
-O keylocation=file:///etc/zfs/zroot.key \
-O keyformat=passphrase \
-o compatibility=openzfs-2.1-linux \
-m none zroot "${POOL_ID}"
4.4 Create ZFS file systems
zfs create -o mountpoint=none zroot/ROOT
zfs create -o mountpoint=/ -o canmount=noauto zroot/ROOT/${ID}
zfs create -o mountpoint=/home zroot/home
4.5 Set preferred boot file system
zpool set bootfs=zroot/ROOT/${ID} zroot
4.6 Export and re-import the pool
zpool export zroot
zpool import -N -R /mnt zroot
4.7 Mount root and home
zfs load-key -L prompt zroot
zfs mount zroot/ROOT/${ID}
zfs mount zroot/home
Verify mountpoints:
# mount -t zfs
zroot/ROOT/debian on /mnt type zfs (rw,relatime,xattr,posixacl)
zroot/home on /mnt/home type zfs (rw,relatime,xattr,posixacl)
4.8 Update device symlinks
udevadm trigger
5. Install Debian
debootstrap bookworm /mnt
Copy files into the new install:
cp /etc/hostid /mnt/etc/
cp /etc/resolv.conf /mnt/etc/
mkdir /mnt/etc/zfs
cp /etc/zfs/zroot.key /mnt/etc/zfs/
Mount:
mount -t proc proc /mnt/proc
mount -t sysfs sys /mnt/sys
mount -B /dev /mnt/dev
mount -t devpts pts /mnt/dev/pts
6. Configure Debian
6.1 Chroot into the new install
chroot /mnt /bin/bash
6.2 Set hostname
echo 'YOURHOSTNAME' > /etc/hostname
echo -e '127.0.1.1\tYOURHOSTNAME' >> /etc/hosts
6.3 Network
Display all detected network interfaces along with their IP and MAC addresses:
ip addr
Debian's network interfaces are configured in /etc/network/interfaces
and controlled by the ifup
and ifdown
commands.
Example entry for a wired interface using DHCP:
allow-hotplug enp0s31f6
iface enp0s31f6 inet dhcp
For wireless:
allow-hotplug wlp61s0
iface wlp61s0 inet dhcp
wpa-ssid <wifi_access_point_name>
wpa-psk <wifi_passphrase>
NOTE: If relying solely on a wireless interface for network access, be sure to install the necessary packages for the wireless device. See the Debian Wiki for details.
6.4 Package repositories
Debian uses separate archives to distinguish between software packages based on their licenses:
- Main is enabled by default and includes everything that satisfies the conditions of the Debian Free Software Guidelines.
- Contrib packages are open-source themselves but rely on software in non-free to work.
- Non-free contains packages that do not meet all the conditions of the DFSG but can be freely distributed.
- Non-free-firmware (introduced in Debian 12) contains non-free firmware binaries packaged for devices that would be completely non-functional without them (example: many wireless cards).
- Backports contains packages drawn from the testing (and sometimes unstable) archive and modified to work in the current stable release.
All backports are disabled by default (to prevent unintended system upgrades) and are installed on a per PACKAGE basis by running:
apt -t bookworm-backports install PACKAGE
Modify /etc/apt/sources.list
to add contrib, non-free, non-free-firmware, and backports:
# cat <<EOF > /etc/apt/sources.list
> deb http://deb.debian.org/debian/ bookworm main contrib non-free non-free-firmware
> deb-src http://deb.debian.org/debian/ bookworm main contrib non-free non-free-firmware
>
> deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
> deb-src http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
>
> deb http://deb.debian.org/debian/ bookworm-updates main contrib non-free non-free-firmware
> deb-src http://deb.debian.org/debian/ bookworm-updates main contrib non-free non-free-firmware
>
> deb http://deb.debian.org/debian/ bookworm-backports main contrib non-free non-free-firmware
> deb-src http://deb.debian.org/debian/ bookworm-backports main contrib non-free non-free-firmware
> EOF
Any time sources.list
is modified its necessary to update the package database:
apt update
6.5 Install tools
apt install -y console-setup cryptsetup curl dosfstools efibootmgr keyboard-configuration locales sudo
6.5.1 Optional: Network manager
Install a network manager utility. Package network-manager
provides the nmcli
console client. Comment out (deactivate) any entries in interfaces
that will be managed by network-manager
.
6.5.2 Optional: Remote access using OpenSSH
apt install -y openssh-server
SSH is enabled by default to launch at boot.
6.6 Set timezone
dpkg-reconfigure tzdata
6.7 Configure locales
dpkg-reconfigure locales
NOTE: Always enable the en_US.UTF-8
locale because some programs require it.
6.8 Console fonts
dpkg-reconfigure console-setup
setupcon
6.9 Keymap
dpkg-reconfigure keyboard-configuration
6.10 Set root password
passwd
6.11 Create user account
Example: username foo
:
adduser foo
7. Configure EFI partition and encrypted swap
7.1 Create vfat filesystem
mkfs.vfat -F32 $EFI_DISK
7.2 Configure swap
Modify /etc/crypttab
:
echo "swap /dev/disk/by-partlabel/swap /dev/urandom swap,offset=2048,cipher=aes-xts-plain64,size=512" >> /etc/crypttab
This will map /dev/disk/by-partlabel/swap
to /dev/mapper/swap
as a swap partition that can be added to /etc/fstab
.
7.3 Create fstab entries
# cat << EOF > /etc/fstab
> $( blkid | grep "$EFI_DISK" | cut -d ' ' -f 2 ) /boot/efi vfat defaults 0 0
> /dev/mapper/swap none swap defaults 0 0
> proc /proc proc defaults 0 0
> EOF
7.4 Mount EFI partition
mkdir -p /boot/efi
mount /boot/efi
8. ZFS configuration
8.1 Install
apt install -y linux-headers-amd64 linux-image-amd64 zfs-initramfs dosfstools
echo "REMAKE_INITRD=yes" > /etc/dkms/zfs.conf
8.2 Enable systemd services
systemctl enable zfs.target
systemctl enable zfs-import-cache
systemctl enable zfs-mount
systemctl enable zfs-import.target
8.3 Configure initramfs
echo "UMASK=0077" > /etc/initramfs-tools/conf.d/umask.conf
Because the encryption key is stored in the /etc/zfs
directory, it will automatically be copied into the initramfs.
Rebuild initramfs:
update-initramfs -c -k all
9. Install and configure ZFSBootMenu
9.1 Set ZFSBootMenu properties on datasets
Assign command-line arguments to be used when booting the kernel. Because ZFS properties are inherited, assign the common properties to the ROOT
dataset so all children datasets will inherit the arguments by default:
zfs set org.zfsbootmenu:commandline="quiet loglevel=4" zroot/ROOT
Setup key caching in ZFSBootMenu:
zfs set org.zfsbootmenu:keysource="zroot/ROOT/${ID}" zroot
To quickly discover and import pools on boot, set a pool cachefile
:
zpool set cachefile=/etc/zfs/zpool.cache zroot
9.2 Install
Fetch a prebuilt ZFSBootMenu EFI executable and save it to the EFI system partition:
mkdir -p /boot/efi/EFI/ZBM
curl -o /boot/efi/EFI/ZBM/VMLINUZ.EFI -L https://get.zfsbootmenu.org/efi
cp /boot/efi/EFI/ZBM/VMLINUZ.EFI /boot/efi/EFI/ZBM/VMLINUZ-BACKUP.EFI
9.3 Configure EFI boot entries
mount -t efivarfs efivarfs /sys/firmware/efi/efivars
efibootmgr -c -d "$EFI_DISK" -p "$EFI_PART" -L "ZFSBootMenu (Backup)" -l '\EFI\ZBM\VMLINUZ-BACKUP.EFI'
efibootmgr -c -d "$EFI_DISK" -p "$EFI_PART" -L "ZFSBootMenu" -l '\EFI\ZBM\VMLINUZ.EFI'
10. Finish up
10.1 Exit the chroot
exit
umount -n -R /mnt
10.2 Export the zpool and reboot
zpool export zroot
reboot
10.3 First boot
User is prompted for the passphrase to unlock the encrypted zpool.
Login and welcome to Debian!
10.3 Where to go next ...
... is up to YOU. Enjoy!
How about a laptop home server? Or a lightweight desktop using Openbox?
Whatever you decide to do, I hope you enjoy Debian!
11. Resources
- ZFSBootMenu: Debian Bookworm (12) UEFI
- Detailed installation guides for setting up ZFS on several Linux distros. Immensely helpful!
- Minimal Debian
- An alternate method I've used to setup a base Debian install using LUKS + ext4 filesystem.
- Encrypt a swap partition
- Encrypted root may pass sensitive information to an unencrypted swap partition that survives a system reboot.
You can like, share, or comment on this post on Mastodon 💬
» Next: Set charging thresholds on Linux laptops to extend battery lifespans
« Previous: Minimal Debian Bookworm