BTRFS: Smooth as Butter
I have a habit of calling BTRFS “butter-F-S.” Conveniently in text I don’t feel a need to say that, because it’s easier to type out BTRFS than “butter-F-S”, as opposed to being easier to say the latter than the former. Regardless, BTRFS is a file system, which can be thought of as the organization system used by a hard drive to store files. File systems provide the functionality necessary for handling data; without one, data would exist on a disk with no means of (simple, reliable) access, management, or use. Every operating system provides the necessary configurations for using at least one file system, and often times can be expanded to understand more file systems, as is the case with Linux. If you’re used to Windows, you’ll be primarily familiar with two file systems: NTFS, and FAT. If you’re familiar with Linux, you’ll have probably dealt with those, as well as EXT. If you’re adventerous, you have have tried additional file systems such as ZFS, or BTRFS.
When I returned to Linux full-time on my desktop, I decided I wanted to setup a storage system. I initially shopped around for a NAS: network-attached storage. This would be a separate device, basically a motherboard with hard-drives. It would include software for storing data reliably, as well as applications for serving that data, such a Plex. There are many top-rated off-the-shelf options available, but many are costly, propietary, and lock you in to that solution. I decided to go with something a bit more readily available, and turn two existing 3 terrabyte drives into a storage system that would live as part of my desktop. The remainder of this post will deal with how I setup BTRFS on my Linux desktop, using sub-volumes, creating automated snapshots, and setting up a back-up schedule.
Setting up BTRFS
Linux has “first-class” support for BTRFS, which was a deciding force between it
and ZFS. (Though, recently, ZFS has made some strides as well.) The only
requirements necessary for using BTRFS is to install the btrfs-progs
program,
which is required for basic operations. With requirements done, the next step is
to setup the filesystem on your disk of choice. This will delete all information
on your disk, so only do this when you’re certain any existing data has been
backed-up, or you don’t mind losing it.
mkfs.btrfs /dev/partition
I decided to go with a partitionless setup, which is a slightly modified version
of the above command. The above command also allows for adding a disk label, as
well as a few other options; man mkfs.btrfs
will give you all the details. I
decided to call my BTRFS storage system my “Bag of Holding.”
mkfs.btrfs -L BagOfHolding /dev/sdg
Creating a partitionless setup removes the MBR or GPT partitioning schemes, and relies on subvolumes to simulate partitions. Because I’m only using these disks for storage, and I won’t be booting from them, this seemed like the way to go.
My setup will take two drives, and combine them together into a RAID-1. In order to allow for me to get the data from the drives into my new RAID, I did one disk at a time, and moved data between them, I then balanced the RAID.
Configuring a BTRFS RAID
At this point, I have two separate drives. One of my drives has all my data on
it, the other drive is a raw, partitionless filesystem. At this point, we can
leverage BTRFS to combine both our disks into a single “device”, and then
balance it. All these commands will leverage the btrfs
command, which needs to
be run as root.
First, mount one of the drives. In my case, I mounted the drive with data
mount -t btrfs /dev/sdg /mnt/BagOfHolding
Next, I added my second device to the mounted file system
btrfs device add /dev/sdh /mnt/BagOfHolding
At this point, we have a filesystem with two devices, but the data and metadata
hasn’t been balanced yet. To simply balance the data, and replicate a RAID-0
setup, you would run the btrfs balance
command, specifying the
mounted filesystem. In my case, I wanted to replicate a RAID-1 setup, having the
two disks mirrored instead of striped. The command is modified to include a
“balance filter”:
btrfs balance -dconvert=raid1 -mconvert=raid1 /mnt/BagOfHolding
This command will take time, since it has to re-balance the data across the devices. A convenient time for a short aside:
Buzzwords of Butter
- Copy-on-Write (COW): Basically, only make copies to data when there are written changes to it. I don’t fully understand Copy-on-Write, and is possibly a good candidate for a future post.
- Subvolumes: Like a partition, but not a block device. The BTRFS Wiki defines it as “an independently mountable POSIX filetree.” I think of subvolumes as “software partitions” which I’m sure is both wrong and infuriating to people who know more about it than I do.
- Snapshots: A snapshot is a subvolume that shares its data with another subvolume, using copy-on-write. This means if there are no changes to the underlying data, a snapshot is basically just a reference to the exactly same data as the initial subvolume. As changes get made, the snapshot references a copy of “old” data, as opposed to the new data. Thus, a snapshot represents data at a specific point in time.
Setting up Subvolumes
At this point, I have a single device made of two disks. The device, when
queried using btrfs filesystem show
shows the total available and used space,
and the individual disks composing it. Creating subvolumes is optional; by
default, a BTRFS filesystem has one subvolume (with id 5) as the “root.” If you
mount the device, you’ll mount that, and see the entire device. I wanted a bit
more organization, and options for snapshots, so I created a number of
subvolumes for different files: Books, Code, Documents, Games, Misc, Music,
Pictures, Videos. I mount each separately, and then sym-link directories in my
home directory to a corresponding subvolume.
Creating a subvolume is very straight forward, using the brtfs subvolume
create
command. I made many, as mentioned before, and I’ll walk through how I
setup the Books subvolume. I followed the same steps for all other subvolumes.
First, I created it:
btrfs subvolume create /mnt/BagOfHolding/Books
Then, I configured it to automatically mount. This involved adding a line to my fstab file:
...
UUID=658cc4e0-93e1-43b5-b068-d889b44ae98d /mnt/BagOfHolding/Books btrfs subvol=/Books,defaults,nofail,x-systemd.device-timeout=5
...
Looks very similar to other entries, except that the option subvol=/Books
is
necessary! This whole line tells the file system to mount the BTRFS subvolume
located at Books relative to the “root” subvolume, to the mount point
“/mnt/BagOfHolding/Books”. The other important thing to remember is that
subvolumes are not block devices. For the BTRFS device, there is only one block
device, and that’s the RAID we setup earlier. If you run btrfs filesystem show
you’ll see the device has a single UUID, despite having the two individual
disks. In fact, if you were to mount either of the disk devices, you would mount
the raid; in my case, if I were to use /dev/sdg
or /dev/sdh
instead of the
UUID, it would do the same thing. UUIDs are more reliable, though, so I tend
towards them. My fstab has a line like the above for each subvolume. Once that’s
done, unmount the RAID, and then either run mount -a
or restart to get each
individual subvolume mounted. The final step I did was to symbolic link
directories from my home directory to the corresponding subvolumes. Following
with Books, I did ln -s /mnt/BagOfHolding/Books Books
from my home directory.
Now, if I cd ~/Books
I get to the subvolume on my RAID.
Scheduling Snapshots
With the RAID established, and subvolumes created, mounted and linked, I now can schedule automatic snapshots. An easy way to do so is with a program called Snapper. Installing that provides the application, as well as schedules both via cron and Systemd. Because I’m running Arch, we’ll rely on the Systemd timer. Before that, we need to create a Snapper configuration.
sudo snapper -c books create-config /mnt/BagOfHolding/Books
This will create the configuration file in “/etc/snapper/configs/”. The configuration includes limits on how many snapshots to keep of different types (“hourly”, “weekly”, etc..). The defaults seemed sane enough for me. Without a cron scheduler, though, nothing else happens. (If you have a cron scheduler, then it will have started automatically and will run accordingly). The final step is to enable and start the “snapper-timeline” timer. If desired, modify the timer frequency (I believe the default is hourly, which is good enough).
One last thing to do for Systemd is to also enable and start the “snapper-cleanup” timer, which will cull snapshots down to the configured amount from the configuraiton file.
An interesting thing about snapshots is that, unless something has changed, they won’t take up space. Creating 10 snapshots will not replicate data 10 times. What each snapshot will capture are any changes that have been made to the data.
Creating Backups from Snapshots
The final phase of my BTRFS journey is to establish backups. One thing that must
be emphasized: SNAPSHOTS ARE NOT BACKUPS. They can be used to make backups,
though. The way I’m doing that currently is with a program called snap-sync.
snap-sync will iterate through each Snapper config, and send a snapshot from
each to a remote BTRFS-formatted source. In my case, the remote source is an
external hard drive. I formatted it similar to my RAID drives, without a
partition. Once done, I ran snap-sync
as root, which provides guidance for
choosing a disk, and walks through each Snapper config. I ran it once, to get
each directory established on the external drive. The manual (man snap-sync
)
includes example Systemd timers, which I used to create a timer and service in
“/usr/lib/systemd/system”. Then, I enabled and started the timer. The example
runs once a week, though I think I may update that to once a day.
Conclusion
With that, I feel I have a good solution to my storage needs. I can keep all my data on a RAID drive with backups, accessible easily from the primary machine I use. I further synchronize music and pictures to and from my phone using Syncthing, which will be an upcoming topic of discussion. Some next steps:
- setup and configure Calibre for my books
- better configure Demlo for my music
- look into accessing my RAID from my Raspberry Pi, perhaps via NFS, and leveraging wake-on-lan, to allow for streaming media remotely whenever, without having to leave my desktop on
I’m writing this post as part of #100DaysToOffload, an initiative to inspire writing habits. Perhaps you could do the same.