While btrfs may be relatively “new” in regards to file systems, it is receiving widespread adoption, being included as the default FS in many distros. They is because it provides many advantages when compared to the traditional luks on lvm encrypted linux install:

  1. Subvolume Snapshots: btrfs is a copy-on-write FS (CoW) meaning changes to an individual file result in a new file being written. This means that each subvolume can be snapshotted, where a complete version of every file is captured at a moment in time. This enables simple backup and rollback in case of failure.
  2. Automatic Compression: btrfs subvolumes can store files with dynamic compression. This is extremely configurable across subvolumes with tradeoffs is speed and compression ratio to support the variance in subvolume usage.
  3. Subvolume Resizing: Files are tracked using simple pointers (actually quite complex, but I know very little and can explain even less). This means that unlike traditional disk partitions, which are defined as a collection of disk blocks, data on a single subvolume may span the entire disk and subvolumes contents are tracked with the file descriptors. This means changing subvolume sizes does not mean physically redefining disk partitions but is rather handled quickly and efficiently in metadata.
  4. Many More: But this is not a post on the merits of btrfs.

The problem is that I run debian stable (current version 11). The beauty (and the curse) of this distribution choice is that things move a little slower. As such, the debian installer does not currently support installing btrfs with subvolumes.

Fortunately, there is a fantastic article by Jake Bauer describing how this can be done in debian 10. Unfortunately, the instructions still configure LUKS on LVM with the btrfs subvolumes in an LVM volume group rather than directly within the LUKS crypt. Therefore, I’ve decided to write up this brief tutorial to provide the later.

Begin the Installation

Push the USB in, and being the installing by choosing Advanced options > Expert install. This is important because it enables us to manually configure btrfs subvolumes later.

Partition Devices

In the partition scheme definition choose Manual partitioning.

We need to create 2 partitions:

  1. EFI partition: I set this to 512MB and the installer should default to vfat and automatically handle /boot/efi mount.
  2. boot partition: This can be 512MB as well and should set the Use as: field to ext2 and set the mount point to /boot.

Then use the Configure encrypted volumes option to create an encrypted LUKS partition with the remaining space. On this LUKS encrypted partition (mine is nvme0n1p3_crypt) set the Use as: field to btrfs journaling file system and the mount point /.

Finish partitioning and write changes to disk

But what about a swapspace?

I typically do not install with swapspace. My machine is equipped with more than adequate RAM and therefore it is only useful for hibernation, which I seldom use. If you want a swapspace, there are a variety of options:

  1. As a separate disk partition.
  2. As an individual btrfs subvolume.
  3. As a btrfs swapfile.

Manually Create and Mount btrfs Subvolumes

One the partitions are written, and BEFORE continuing the installation, we need to manually create and mount the btrfs subvolumes.

There are two trains of though for organizing btrfs subvolumes:

  • hierarchical: subvolumes are defined within other subvolumes.
  • flat: subvolumes are mounted within other subvolumes but are define externally.

I prefer a flat layout, probably because it results in more clearly defined partitions. If you would rather install with a hierarchical system, you can probably figure out the requisite changes.

Begin by pressing Ctrl+Alt+F2 to drop into a shell and press Enter to activate it.

Use the df command to view the current mounted partitions. There should be three important mounts:

  1. The luks encrypted btrfs root subvolume mounted to /target - mine is the device /dev/mapper/nvme0n1p3_crypt.
  2. The boot partition mounted to /target/boot - mine is the device /dev/nvme0n1p2.
  3. The EFI partition mounted to /target/boot/efi - mine is the device /dev/nvme0n1p1.

We need to umount the partitions in reverse order:

~ # umount /target/boot/efi
~ # umount /target/boot
~ # umount /target

Then we mount our existing root subvolume to /mnt.

~ # mount /dev/mapper/nvme0n1p3_crypt /mnt

Create new subvolumes. I have chosen separate for /root, /home, and /var. /snapshots is used to store btrfs snapshots. You can easily update this to your preference.

~ # btrfs subvolume create @
~ # btrfs subvolume create @home
~ # btrfs subvolume create @var
~ # btrfs subvolume create @snapshots

And mount them underneath the installation directory /target. There are tons of different mount configuration options, these are a sane default selection.

~ # mount -o noatime,compress=lzo,space_cache,subvol=@ /dev/mapper/nvme0n1p3_crypt /target
~ # mkdir /target/home
~ # mount -o noatime,compress=lzo,space_cache,subvol=@home /dev/mapper/nvme0n1p3_crypt /target/home
~ # mkdir /target/snapshots
~ # mount -o noatime,compress=lzo,space_cache,subvol=@snapshots /dev/mapper/nvme0n1p3_crypt /target/snapshots
~ # mkdir /target/var
~ # mount -o noatime,compress=lzo,space_cache,subvol=@var /dev/mapper/nvme0n1p3_crypt /target/var

Remount the /boot and /boot/efi partitions.

~ # mkdir /target/boot
~ # mount /dev/nvme0n1p2 /target/boot
~ # mount /dev/nvme0n1p1 /target/boot/efi

Copy over the /etc metadata.

~ # mkdir /target/etc
~ # cp /mnt/etc/fstab /target/etc
~ # cp /mnt/etc/crypttab /target/etc/crypttab

Update the fstab file (now located at /target/etc/fstab) to include our new btrfs subvolume mounts. It should end up looking something like the example below:

# <file system> <mount point>   <type>  <options>       <dump>  <pass>
#/dev/mapper/nvme0n1p3_crypt /               btrfs   defaults,subvol=@rootfs 0       0
# /boot was on /dev/nvme0n1p2 during installation
UUID=f434d69d-c351-498d-bb6c-0b78d44754dc /boot           ext2    defaults        0       2
# /boot/efi was on /dev/nvme0n1p1 during installation
UUID=BF50-85A0  /boot/efi       vfat    umask=0077      0       1
# btrfs subvolumes mounted manually during installation
/dev/mapper/nvme0n1p3_crypt /          btrfs noatime,compress=lzo,space_cache,subvol=@          0 0
/dev/mapper/nvme0n1p3_crypt /home      btrfs noatime,compress=lzo,space_cache,subvol=@home      0 0
/dev/mapper/nvme0n1p3_crypt /snapshots btrfs noatime,compress=lzo,space_cache,subvol=@snapshots 0 0
/dev/mapper/nvme0n1p3_crypt /var       btrfs noatime,compress=lzo,space_cache,subvol=@var       0 0

And finally cleanup the btrfs root, otherwise these directories will remain in the final installation.

~ # rm -r /mnt/boot /mnt/etc /mnt/media
~ # umount /mnt

Once this is done you can exit out of the terminal with Ctrl+D and return to the installation with Ctrl+Alt+F1.

Complete the Installation

The remainder of the installation can be completed as usual. Reboot into your brand new debian 11 installation with LUKS encrypted btrfs subvolumes!

12 day(s) offloaded in the 100DaysToOffload challenge.