BTRFS is one of the contenders for the successor to EXT4, which has already been around for a few years. Today I will show you how to configure BTRFS on a Raspberry Pi 64 bit system in the kernel and how to create an automated backup solution with btrbk.

Advantages of BTRFS

  • Advanced features: BTRFS offers advanced features such as snapshots, dynamic inode allocation, built-in RAID support, and data integrity checks.
  • Snapshot and rollback capability: BTRFS allows you to create snapshots of your data, which can be useful for data backup and recovery.
  • Bit-red self-healing: BTRFS can detect damaged data and, if configured in a RAID setup, automatically repair it.
  • Efficient Copy-on-Write (CoW): This enables efficient backup and cloning processes.

Advantages of btrbk

  • btrbk is tailor-made for backups, adept at transferring snapshots to locations like external drives or remote servers, key for off-site backup.
  • Advanced Retention Policies: Offers detailed retention settings, allowing customization of snapshot preservation across different time frames (hourly to yearly).
  • Automates snapshot creation, transfer, and cleanup based on retention policies, streamlining regular backup operations.

You'll need

  • a Raspberry Pi which has already been converted to 64 bit. Please note that Internet solutions that only show how to convert the kernel to 64 bit offer no real advantages because the user space is not converted. A complete conversion requires a new basic installation of all software on your system.
  • backup hardware, e.g. a NAS.
  • A device for mounting the flash drive.

Before you start

Create a complete backup of your Raspberry Pi, e.g. with dd. There is always something that can go wrong.
These instructions refer to kernel version 6.1.21-v8. You can check your kernel version with the command uname -a.

uname -a
Linux homer4 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr  3 17:24:16 BST 2023 aarch64 GNU/Linux

check kernel version

Debian 12 bookworm was used as the OS. You can see your OS version under /etc/os-release:

cat /etc/os-release 
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

/etc/os-release/etc/os-release

There should be enough free space on your Raspberry Pi file system. As a rule of thumb, about 50 percent. However, the convert process can also be successful with much less. In my case, for example, less than 10 percent disk space was still free.

Preparing Raspberry Pi

If not already available, install intiramfs-tools, btrfs and btrbk:

apt install initramfs-tools btrfs-progs btrbk

Add the btrfs module via initramfs modules in /etc/initramfs-tools/modules by simply adding btrfs at the end of the file:

# List of modules that you want to include in your initramfs.
# They will be loaded at boot time in the order below.
#
# Syntax:  module_name [args ...]
#
# You must run update-initramfs(8) to effect this change.
#
# Examples:
#
# raid1
# sd_mod
btrfs

/etc/initramfs-tools/modules

If you run the mkinitramfs command you get strange errors like "Missing /boot/config-[kernel_version]" and that zstd or gzip is not supported by the current kernel.
mkinitramfs has not yet (as of January 2024) been configured for the RaspberryPi 64Bit, so we have to make a change in the setup file.

In /usr/sbin/mkinitramfs add the following lines below the existing shebang #!/bin/sh:

#!/bin/sh
sudo modprobe configs && zcat /proc/config.gz > /boot/config-$(uname -r)

/usr/sbin/mkinitramfs/usr/sbin/mkinitramfs

and create a new initramfs:

mkinitramfs -o /boot/initramfs-btrfs.gz

Beware: From here, your Raspberry Pi will be switched to BTRFS after restarting. Without changing the file system afterwards, your Raspberry Pi will no longer start.

Now change to directory /boot/ and replace ext4 with btrfs in /boot/cmdline.txt manually with any editor such as nano or vim.

root=<mountpoint root> rootflags=subvol=@root rootfstype=btrfs

Load the new kernel configuration at each boot with the following entry at the end of the configuration in /boot/config.txt:

initramfs initramfs-btrfs.gz

/boot/config.txt

The last change is made in the /etc/fstab. Change the file system for the root partition (not for boot) to btrfs manually with any editor such as vim.

<mountpoint root> /  btrfs    subvol=@root,defaults,noatime  0       1

/etc/fstab

Now shutdown your Raspberry Pi remove your flash drive and plug it into a separate system.

shutdown now

Convert your EXT4 filesystem to BTRFS

Your flash drive must not be mounted and btrfs-progs must be installed on your system.
Check for your drive with lsblk.

lsblk -f                                                                                                               
├─sdd1 vfat    FAT32  bootfs  0B22-0000                                           
└─sdd2 ext4           rootfs  dd569980-a77f-4eaa-0000-0000b2072a8e   

flash drive filesystem

By default, your Raspberry Pi uses two partitions. We want to convert rootfs. bootfs remains untouched. Check carefully if this is really the right partition you want to convert to btrfs. In the example, sdd2 is the correct partition. That doesn't have to be the case for you.

First check the partition for possible errors. This step is not absolutely necessary.

 e2fsck -fvy /dev/sdd2

Then you can use the btrfs-convert function.

btrfs-convert /dev/sdd2 
create btrfs filesystem:
        blocksize: 4096
        nodesize:  16384
        features:  extref, skinny-metadata (default)
        checksum:  crc32c
free space report:
        total:     128048168960
        free:      8599547904 (6.72%)

This process can take a very long time. It is a process consisting of three steps:

  • creating ext2 image file
  • creating btrfs metadata
  • copy inodes

The critical process is the first, because it could fail due to insufficient disk space.
As soon as the process is complete, you will receive the message 'conversion complete'.

If you convert your file system using btrfs-convert, you create a BTRFS filesystem with a two subvolume for ext2_saved and another for rootfs without UUID. It is not possible to create snapshots of subvolumes without UUIDs with btrbk.

Mount your devices locally again and check if everything is ok (it should be).

mount /dev/sdd2 /mnt

Go to your mountpoint /mnt and create a new subvolume for your root device.

btrfs subvolume create @root

Btrfs subvolumes behave like normal directories in Linux. All operations such as rm or ls also work here. We use this fact to migrate the data from root to the new subvolume. By naming convention, subvolumes are preceded by an @ character.

Check if the subvolume creation has worked:

btrfs subvolume show /mnt/@root/
@root
        Name:                   @root
        UUID:                   16197b0e-0000-a24e-0000-9b09c6fbae1b
        Parent UUID:            -
        Received UUID:          -
        Creation time:          2024-01-11 00:28:42 +0100
        Subvolume ID:           396
        Generation:             3795
        Gen at creation:        3794
        Parent ID:              5
        Top level ID:           5
        Flags:                  -
        Snapshot(s):
:

We now need to move the files from / to the new subvolume. Many tutorials on the Internet specify a rsync command for reasons I don't understand. However, a pure mv command is sufficient and much faster.

This command will move all directories on your mounted drive to @root subvolume except @root itself.:

for dir in */; do [ "$dir" != "@root/" ] && mv -v "$dir" "@root/"; done

Migrate root filesystem to btrfs subvolume @root

This will take a very long time, depending on storage consumption but is still faster than rsync.

Boot up Raspberry Pi

Reconnect your flash drive to your raspberry pi and let it boot up.

Test if everything is ok and delete the subvolume ext2_saved since it takes up a lot of space:

cd /
btrfs subvolume delete ext2_saved

delete ext2_saved

btrfs subvolume show / 

	Name: 			<FS_TREE>
	UUID: 			-
	Parent UUID: 		-
	Received UUID: 		-
	Creation time: 		-
	Subvolume ID: 		5
	Generation: 		2032
	Gen at creation: 	0
	Parent ID: 		0
	Top level ID: 		0
	Flags: 			-
	Send transid: 		0
	Send time: 		-
	Receive transid: 	0
	Receive time: 		-
	Snapshot(s):

As you can see, there is no snapshot yet. Create the first snapshot with the command:

btrfs subvolume snapshot / snapshots

This creates a snapshot in the /snaphosts directory which hardly takes up any disk space.

Avoid the BTRFS swapfile problem

If you are using a swap file, you will need to make additional changes. The steps are described very well in the official documentation but despite these measures it is not possible to create snapshots without first deactivating the swap. You get the error message:

ERROR: cannot snapshot '/': Text file busy

Theoretically, this could be avoided by creating a separate subvolume for the swap file.
Another way is to use zram as an alternative to swapfile. zram allocates swap in RAM. A small positive side effect is the performance gain.

The configuration is simple.

apt install zram-tools

Comment out the line PERCENT=50 in the file /etc/default/zramswap and enable the service:

systemctl enable --now zramswap.service

Backup with btrbk

Create a new file /etc/btrbk/btrbk.conf and add the following to it:

transaction_log /var/log/btrbk.log
timestamp_format short
snapshot_dir SNAPSHOTS
snapshot_create always

volume /
    subvolume /
    snapshot_preserve_min 7d
    snapshot_preserve 7d

/etc/btrbk/btrbk.conf

We now need a snapshot directory. In the configuration above I called this SNAPSHOT. btrbk does not create this directory, so we have to do that:

mkdir /SNAPSHOTS

Now we can test the configuration:

btrbk -c /etc/btrbk/btrbk.conf run

A successful test should show the following:

--------------------------------------------------------------------------------
Backup Summary (btrbk command line client, version 0.32.5)

    Date:   Thu Jan 11 00:21:08 2024
    Config: /etc/btrbk/btrbk.conf

Legend:
    ===  up-to-date subvolume (source snapshot)
    +++  created subvolume (source snapshot)
    ---  deleted subvolume
    ***  received subvolume (non-incremental)
    >>>  received subvolume (incremental)
--------------------------------------------------------------------------------
/
+++ /SNAPSHOTS/ROOT.20240111

The /etc/btrbk directory also contains a more detailed example configuration.
It is also possible, for example, to perform both pull and push backups remotely with btrbk via SSH.
The file is called btrbk.conf.example and is well commented.
The current configuration will create 7 backups with a daily time span.

However, we also need to trigger the backup process. To do this, we create a SystemD unit configuration file in /etc/systemd/system

[Unit]
Description=Daily btrbk snapshot

[Service]
Type=oneshot
ExecStart=/usr/bin/btrbk -c /etc/btrbk/btrbk.conf

btrbk-local-snapshot.service

[Unit]
Description=Run btrbk.service daily at 23:55

[Timer]
OnCalendar=*-*-* 23:55:00
Persistent=true

[Install]
WantedBy=timers.target

btrbk-local-snapshot.timer

In the example, a snapshot is taken every night at 23:55.

Restore the snapshot

sudo btrfs subvolume delete /mnt/@root
sudo btrfs subvolume snapshot /mnt/path/to/btrbk/snapshots/@root.20210101 /mnt/@root

What you need to bear in mind

As long as BTRFS is not in the kernel by default, you will have to make the adjustments to initramfs again with every kernel update (mkinitramfs step). However, this will become faster and faster over time.