With a recent reinstall of Ubuntu 20.04 on my personal computer, I decided that I wanted to explore a new type of filesystem, and find a replacement for how I used the traditional ext4 and LVM - and I settled on btrfs. While I was and am excited about the power of this next-generation filesystem, I've discovered that it doesn't run itself - with powerful features comes complexity. Over the next few weeks, I plan to write a few posts on what I've learned about btrfs, how to manage and maintain it, and a few things you may not expect if you are used to running Ubuntu with ext4 and LVM.
The computer I am testing on has four disks: one 1TB, one 2TB SSD, and two additional 512GB NVMe SSDs. The 1TB and 2TB disks are managed by a hardware RAID controller.
It's a personal computer used for gaming and daily use, as well as a place I experiment. It runs a small single-node Kubernetes cluster which I use for automated builds of some of my repos, for running Folding@Home, and several other services. You can learn more about this machine and my other infrastructure from the README for my homelab's repository.
During the reinstall, I also chose to dual-boot Windows and Linux, splitting the main disk in two: 200GB for the Ubuntu root (
/) and 800GB for Windows. The two 512GB drives don't have a hardware RAID controller, but I wanted to use those in RAID1 for the
/home folder in Ubuntu.
I also have spent a significant amount of time in the past testing different systems of backup and restore for this machine, out of curiosity, including Ubuntu's built-in Deja Dup, duplicacy, and full-disk backups with
tar, and looked forward to some of the features btrfs provides to make backups easier.
So, with these goals in mind, what are some of the features btrfs provides to make this happen?
btrfs - a next-generation filesystem
btrfs was developed beginning in 2007, and merged into the Linux kernel mainline in 2009, and declared stable in 2013. The name has various pronunciations, and is a reference to the filesystem's core data structure: a copy-on-write (COW) B-tree.
Built-in to btrfs are a number of features you may not expect the filesystem to provide for you. Commonly-desired features that Linux users would install additional software for have been built into the filesystem from the beginning.
btrfs provides users some ways to manage physical devices directly, like LVM, as well as providing some support for software-RAID arrangements, like mdadm. Though not meant to compete feature-for-feature, btrfs supports:
- Creating a filesystem with metadata, data, or both in RAID 0, RAID 1, RAID 10, RAID 5 and RAID 6
- Performing day-2 addition of disks to an existing filesystem, and conversion between RAID levels
- Resizing the filesystem to take advantage added disks, or decrease the size of the filesystem to account for lost disks
- Balancing data across disks, including when replacing disks due to failure
btrfs doesn't provide complex volume group and logical device management like LVM - instead, all devices are joined into a common pool, and individual pieces of data are arranged onto the storage pool according to RAID configuration.
However, though devices are pooled within each btrfs filesystem, it is possible to have multiple filesystems active, and the command
btrfs device scan searches all devices for distinct btrfs filesystems.
The Ubuntu installer creates one btrfs filesystem for the root directory. I placed this on a 200GB partition of the hardware RAID controlled pair of disks. I moved the home directory to a second filesystem using btrfs' RAID:
$ sudo btrfs filesystem show Label: none uuid: 3451815e-07c2-4b60-bd43-68fd338aa881 Total devices 1 FS bytes used 172.82GiB devid 1 size 195.31GiB used 177.03GiB path /dev/md126p5 Label: none uuid: af5e3ee6-40c6-4dc0-82f3-5f6a025f842c Total devices 2 FS bytes used 49.55GiB devid 1 size 465.76GiB used 83.03GiB path /dev/nvme0n1 devid 2 size 465.76GiB used 83.03GiB path /dev/nvme1n1
btrfs allows users to create multiple subvolumes within a filesystem. btrfs subvolumes resemble as folders within the filesystem, and can be nested within each other. The mounted filesystem within which you create the subvolume is the subvolume's parent. Mounting a parent subvolume implicitly mounts the child subvolumes at their path. Each subvolume has a UUID, a unique ID, and is also uniquely identified by its
name, which is also the path at which it will appear under its parent. Because this
name is also the subvolume's path, moving a subvolume is the same as renaming it.
However, subvolumes differ from folders in a number of ways:
- Subvolumes can be individually mounted at another location
- Subvolumes are globally queryable with
btrfs subvolume list <path>
All btrfs filesystems have at least one subvolume: this is the root subvolume, and by convention has ID=5 and the hidden name
FS_TREE. This root subvolume is not included in
btrfs subvolume list, but in filesystems with additional subvolumes, it will be the ultimate parent of any child subvolumes. The
top level field of
btrfs subvolume list indicates the ID of the parent subvolume. With
path field shows the name of the parent as the first part of the path.
Any subvolume within the filesystem can be mounted as the root, not just
FS_TREE. Ubuntu, in fact, creates a child subvolume with the name
@ and mounts this subvolume to the path
/ instead of mounting
FS_TREE to that location.
For more help understanding subvolumes and when to use them, check out the btrfs SysadminGuide.
Because btrfs is copy-on-write, it supports lightweight snapshots which capture the current state and only record the changes made to the filesystem since the previous snapshot.
These snapshots are actually new subvolumes, identical to plain subvolumes except that they are populated with the content of their source at creation. This is done without consuming any space initially because the new subvolume simply references the data without making a new copy. Snapshots live within the filesystem as subvolumes, and are mountable and browseable like others.
Read-only snapshots can maintain the state of the filesystem at a fixed point in time, while read-write snapshots can restore such a snapshot and be used to recover.
On top of these features, btrfs also supports automatic compression using one of several algorithms: zlib, lzo, and zstd. Compression can be activated at mount-time, either using a btrfs-designed heuristic for when files are compressive by specifying
-o compress (though some users do not recommend this approach), or on all files by specifying
-o compress-force. You can also use extended attributes to enable or disable compression on individual files using the command
btrfs property set <file> compression ... or
While btrfs has some amazing features, it can make familiar tools behave in unexpected ways - including old standbys like
df! In the next part, I'll walk through some of the best tools to interact with btrfs and maintain it in the long-term.
Credit for the title image to CyHawk - Own work based on ., CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=11701365