If you're interested in trying the btrfs filesystem (as an alternative to ext4) on your Raspberry Pi, you've come to the right some place. This procedure assumes your system is running the trixie release of RPi OS. If you're running another OS, you're welcome to try this procedure, but please keep in mind that it was written for RPi OS 'trixie' running on RPi hardware. FWIW, the "target" system hardware here is an RPi Zero 2W, but the conversion procedure should work for any system.
Let's get to it... As currently written this procedure uses two (2) RPis. However, it's also "do-able" on a single system. I have done this btrfs conversion by creating new partitions on a 2nd SD card, and using rsync to copy from the original ext4 source SD. An alternative to that approach is to use the btrfs-convert utility (p/o the btrfs-progs package) on the un-mounted ext4 source partition. I've elected not to include that alternative here, but if you're interested, let me know; I'll clean it up and post it here.
You will need a 2nd SD card for this procedure; you will also need two (2) USB-SD adapters. We will refer to this 2nd SD card as SD2. I used a SanDisk 64GB SDXC microSD card for SD2. Please note that this procedure will not modify your current trixie system (other than to install two software packages via apt), nor will it modify your original SD card - SD1. In this way, you have a "fallback" - your original system remains "as-is" on SD1 in case something goes wrong, or you simply decide after a short trial that btrfs is not for you.
Again, I used two RPis here for my convenience. I refer to these two RPis as the TARGET RPi, and the SUPPORT RPi; the TARGET RPi is the RPi that will have its root filesystem converted to btrfs.
-
On the TARGET RPi, use
aptto update, upgrade & install two packages, and addbtrfssupport to yourinitramfs:sudo apt update sudo apt -y full-upgrade # optional sudo apt install btrfs-progs initramfs-tools # open editor & add the word `btrfs` to /etc/initramfs-tools/modules, or: sudo echo "btrfs" >> /etc/initramfs-tools/modules # apply the change: sudo update-initramfs -u # issue 'halt' to TARGET RPi, remove SD card (SD1) & insert SD1 into USB-SD adapter sudo halt -
On the SUPPORT RPi: Plug SD1 and SD2 into USB ports; verify using
lsblk --fs.lsblk --fs NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS sda ├─sda1 vfat FAT32 bootfs 1C94-4EC3 └─sda2 ext4 1.0 rootfs f0abac56-08be-42e2-8726-9baa083e8685 sdb └─sdb1 exfat 1.0 64GB-SD 6FDB-2FBD nvme0n1 ├─nvme0n1p1 vfat FAT32 bootfs 91FE-7499 434.5M 15% /boot/firmware └─nvme0n1p2 ext4 1.0 rootfs 56f80fa2-e005-4cca-86e6-19da1069914d 428.6G 1% / -
On the SUPPORT RPi: use
fdiskto obtain needed information on/dev/sda(SD1).# get information on SD1 from fdisk: sudo fdisk -l /dev/sda Disk /dev/sda: 59.48 GiB, 63864569856 bytes, 124735488 sectors Disk model: STORAGE DEVICE Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x26576298 Device Boot Start End Sectors Size Id Type /dev/sda1 16384 1064959 1048576 512M c W95 FAT32 (LBA) /dev/sda2 1064960 124735487 123670528 59G 83 Linux -
On the SUPPORT RPi: use
fdiskto create three (3) partitions on/dev/sdb(SD2):sudo fdisk /dev/sdb # "blank" Command inputs are actually 'Enter' to accept default ... Command (m for help): o Created a new DOS (MBR) disklabel with disk identifier 0x6a99c805. The device contains 'gpt' signature and it will be removed by a write command. See fdisk(8) man page and --wipe option for more details. Command (m for help): p Disk /dev/sda: 59.48 GiB, 63864569856 bytes, 124735488 sectors Disk model: STORAGE DEVICE Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x6a99c805 Command (m for help): n Partition type p primary (0 primary, 0 extended, 4 free) e extended (container for logical partitions) Select (default p): Partition number (1-4, default 1): First sector (2048-124735487, default 2048): Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-124735487, default 124735487): +512M Created a new partition 1 of type 'Linux' and of size 512 MiB. Partition #1 contains a vfat signature. Do you want to remove the signature? [Y]es/[N]o: y The signature will be removed by a write command. Command (m for help): n Partition type p primary (1 primary, 0 extended, 3 free) e extended (container for logical partitions) Select (default p): Partition number (2-4, default 2): First sector (1050624-124735487, default 1050624): Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-124735487, default 124735487): +50G Created a new partition 2 of type 'Linux' and of size 50 GiB. Command (m for help): n Partition type p primary (2 primary, 0 extended, 2 free) e extended (container for logical partitions) Select (default p): Partition number (3,4, default 3): First sector (105908224-124735487, default 105908224): Last sector, +/-sectors or +/-size{K,M,G,T,P} (105908224-124735487, default 124735487): +6G Created a new partition 3 of type 'Linux' and of size 6 GiB. Command (m for help): p Disk /dev/sda: 59.48 GiB, 63864569856 bytes, 124735488 sectors Disk model: STORAGE DEVICE Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x6a99c805 Device Boot Start End Sectors Size Id Type /dev/sda1 2048 1050623 1048576 512M 83 Linux /dev/sda2 1050624 105908223 104857600 50G 83 Linux /dev/sda3 105908224 118491135 12582912 6G 83 Linux Filesystem/RAID signature on partition 1 will be wiped. Command (m for help): t Partition number (1-3, default 3): 1 Hex code or alias (type L to list all): c Changed type of partition 'Linux' to 'W95 FAT32 (LBA)'. Command (m for help): p # this is what yours should look like Disk /dev/sda: 59.48 GiB, 63864569856 bytes, 124735488 sectors Disk model: STORAGE DEVICE Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x6a99c805 Device Boot Start End Sectors Size Id Type /dev/sdb1 2048 1050623 1048576 512M c W95 FAT32 (LBA) /dev/sdb2 1050624 105908223 104857600 50G 83 Linux /dev/sdb3 105908224 118491135 12582912 6G 83 Linux Filesystem/RAID signature on partition 1 will be wiped. Command (m for help): v No errors detected. Remaining 6244352 unallocated 512-byte sectors. Command (m for help): w The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks. -
On the SUPPORT RPi: copy from SD1 to SD2, and format SD2:
# we take care of the `/boot/firmware` partition first (`dev/sdb1`); # copy the boot partition from SD1 (/dev/sda1) to SD2 (/dev/sdb1): sudo dd if=/dev/sda1 of=/dev/sdb1 bs=512 1048576+0 records in 1048576+0 records out 536870912 bytes (537 MB, 512 MiB) copied, 29.9509 s, 17.9 MB/s # we now format /dev/sdb2 and /dev/sdb3 as btrfs sudo mkfs.btrfs -L rootfs /dev/sdb2 btrfs-progs v6.14 See https://btrfs.readthedocs.io for more information. NOTE: several default settings have changed in version 5.15, please make sure this does not affect your deployments: - DUP for metadata (-m dup) - enabled no-holes (-O no-holes) - enabled free-space-tree (-R free-space-tree) Label: rootfs UUID: 6112e919-f036-4da2-91c7-7bfaedd2c145 Node size: 16384 Sector size: 4096 (CPU page size: 16384) Filesystem size: 50.00GiB Block group profiles: Data: single 8.00MiB Metadata: DUP 256.00MiB System: DUP 8.00MiB SSD detected: no Zoned device: no Features: extref, skinny-metadata, no-holes, free-space-tree Checksum: crc32c Number of devices: 1 Devices: ID SIZE PATH 1 50.00GiB /dev/sdb2 # we now mount /dev/sda1 (SD1) and /dev/sdb1 (SD2) so that we can copy the contents # of /dev/sda1 to /dev/sdb1 via rsync: sudo mkdir -p /mnt/SD1 /mnt/SD2 sudo mount /dev/sda2 /mnt/SD1 && sudo mount /dev/sdb2 /mnt/SD2 sudo rsync -HAXav /mnt/SD1/ /mnt/SD2/ > rsync-log.txt 2>&1 sudo umount /mnt/SD1 # remove SD1, label it and set it aside for future use # peruse rsync-log.txt to verify a lof of files were copied! :) # format /dev/sdb3 as btrfs to use as an "extra space" to house snapshots, etc. sudo mkfs.btrfs -L BTRFS1 /dev/sdb3 # verify w/ lsblk --fs lsblk --fs NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS sda ├─sda1 vfat FAT32 bootfs C73C-AF74 └─sda2 btrfs rootfs b90c7819-cd45-4006-9d7b-7407f27085f0 sdb ├─sdb1 vfat FAT32 bootfs C73C-AF74 ├─sdb2 btrfs rootfs 6112e919-f036-4da2-91c7-7bfaedd2c145 └─sdb3 btrfs BTRFS1 ed7c6c3f-4ae7-4113-ae54-0b5891e03fbe nvme0n1 ├─nvme0n1p1 vfat FAT32 bootfs 91FE-7499 434.5M 15% /boot/firmware └─nvme0n1p2 ext4 1.0 rootfs 56f80fa2-e005-4cca-86e6-19da1069914d 428.6G 1% / -
On the SUPPORT RPi: Minor edits to make to make SD2 bootable:
# /mnt/sdb2 should still be mounted at /mnt/SD2, so make required changes there first: sudo nano /mnt/SD2/etc/fstab # or use your preferred editor to make these changes: # FROM: PARTUUID="whatever-1" /boot/firmware vfat defaults 0 2 PARTUUID="whatever-2" / ext4 defaults,noatime 0 1 # TO: LABEL=bootfs /boot/firmware vfat defaults 0 2 LABEL=rootfs / btrfs defaults,noatime 0 0 # PARTUUID="whatever-1" /boot/firmware vfat defaults 0 2 # PARTUUID="whatever-2" / ext4 defaults,noatime 0 1 # now mount /dev/sdb1 for another edit to cmdline.txt : sudo mount /dev/sdb1 /mnt/SD1 sudo nano /mnt/SD1/cmdline.txt # FROM: console=serial0,115200 console=tty1 root=PARTUUID=whatever1 rootfstype=ext4 fsck.repair=yes rootwait cfg80211.ieee80211_regdom=US #TO: console=serial0,115200 console=tty1 root=LABEL=rootfs rootfstype=btrfs fsck.repair=no rootwait cfg80211.ieee80211_regdom=US # C'est terminé!! You should be able to remove SD2 from the SUPPORT RPi, # insert it into the TARGET RPi and boot from it.C'est terminé! ... You should now be able to remove SD2 from the SUPPORT RPi, insert it into the TARGET RPi and boot from it. And a quick comment on the above changes: Note that I used
LABELs instead ofPARTUUIDs. That's just a personal preference; you may usePARTUUID(or something else inls -l /dev/disk, or via theblkidcommand). If I had hundreds of RPi, constantly swapping SD cards, I suppose I might findPARTUUIDs useful... for my present purposes,LABELs mostly work fine. :)
Hopefully, you now have a working (boot-able) btrfs on your RPi. And now that you're here, you may be wondering, "OK... what next?" Well, that's a good question, but unfortunately I don't have a good answer at present - other than, "use it as it is - instead of ext4" ! So far, I've not found switching to btrfs a big change at all... everything I did under ext4 I am still able to do under btrfs. However - I am currently exploring some alternatives... I hope to have something more intelligent to say soon, but as of now - not much. I'll also opine that the vast majority of documentation on btrfs, and how to use it (e.g. for "snapshots") to be inscrutable gobbledygook. Put another way, in one of the many pieces I've read recently, the author simply stated it this way, "Taking snapshots with btrfs is easy; managing those snapshots is not".
- Re Step 6 above, the edit to
/etc/fstab: The sixth field in a/etc/fstabline is known as 'fs_passno', and is used to determine the order thatfsckis run. As there is no benefit to runningfsckon abtrfspartition, 'fs_passno' is changed from1to0. And yes, this is redundant withcmdline.txt... Why? You can ask "The Raspberries" - I don't really know. - Re Step 6 above, the
fsck.repairedit tocmdline.txt:fsck.repairis set "=no" becausefsckis not needed forbtrfs. - Re Step 6; use of LABELs vs PARTUUIDs vs UUIDs, etc, etc. This may be useful to some: The folder
/dev/diskcontains nine (9) sub-folders dedicated to the various methods for referring to a device that is a "disk". If you want to use something other than LABEL, you can find it here. - Re Step 1; the modification of
initramfs: Some accounts on the Internet state that changes made toinitramfsare ephemeral; lasting only until the next kernel upgrade. IOW, when the kernel is upgraded (e.g. inapt), any changes made to initramfs must be re-applied to remain effective under the new/upgraded kernel. In researching this, I found a post in the RPi GitHub Issues that seems to address the question, but like some other posts from this knob, it is inscrutable. IOW this "answer" was unclear, and we may have to wait for a kernel upgrade to learn the answer. If you know the answer - please share! In the meantime, this may help:lsinitramfs /boot/initrd.img-$(uname -r) | grep btrfs - The ability of
btrfsto make "snapshots" of the file system is obviously a major attraction for those that like to experiment with their RPi systems. I am still learning the configuration process, but I hear of an app namedsnapperthat is said by some to be quite good. I'll post a follow-up to this recipe once I've "found my footing".
If you want to capitalize on the potential advantages of btrfs, there is quite a lot to learn! This list of references is deliberately brief, but hopefully helpful toward an understanding. I've also included a couple of "assessment articles" on btrfs ICYI. More detailed references may follow.
-
The Btrfs filesystem: An introduction: A series of articles from LWN (Linux Weekly News), published in 2013-2014. Note that sequels to this article are listed at the end of the article.
-
Working with Btrfs – General Concepts: A series of articles from Fedora Magazine, published in 2022-2023.
-
Debian's btrfs Wiki: There are several good
btrfswikis; this one was selected for obvious reasons :) Check the wiki's change log for the update history. -
An assessment: Examining btrfs, Linux's perpetually half-finished filesystem, from ars technica, dtd Sep, 2021
-
An assessment: Is Btrfs still unstable?, from Darwin's Data, dtd Oct 2023