Upgrading Raspberry Pi OS in minutes when running on btrfs
So Raspberry Pi OS based on Debian Trixie came out and it was time to upgrade.
Thanks to my setup, I was able to do this in minutes without event touching the Pi.
Well, that’s a lie. Next time it’s gonna be a few minutes and no physical contact. 🤞
I had the necessary setup, but I did not have the commands ready, so it took me like 2 hours and I messed up one command, so I needed to do a small manual intervention.
This writeup is as much for everyone else as it is for me for next time. 🙏
How do we do this? What are the options we have?
Options
Assume you have (as I do) 1 drive with system and data and you want to upgrade.
There are 2 basic options:
- write the
.imgto the drive (SD card / SSD), - upgrade using apt which is not recommended, even highly discouraged …
… and they both suck.
With option 1 you would overwrite the whole drive including your data. That is dumb as fuck. 🙉 It would work in a setup where you have 2 drives, one for the OS (or an SD card) and the other for your data. In this setup you could install the new OS onto a new SD card / drive and just swap the SD cards while your Pi is off. But that is not what we are doing … let’s go back to our 1-drive setup. 🔙
Okay, maybe you are thinking that separate partitions for bootfs, rootfs and data would allow you to simply copy the new files (and tweak a few settings) to their respective partitions and go.
But then there is no rollback option.
Okay, you could have made 2 partitions for roofs A and B so you could copy the new system to B and simply change the bootfs to boot from there. That’s smart! But you need to make sure not only 1, but 2 rootfs partitions are big enough for the system, all the packages you might need in the future and there is enough free space for the file system to live happily. That is a waste.
This is all ridiculous. We can just use btrfs and its subvolumes to do this easily. We can have a subvolume for each Raspberry Pi OS version. That eats no extra space. And we can switch versions just by having the respective version of bootfs.
… so assuming we are already on btrfs, let’s do this. If you are not running on btrfs, you might consider it now, so you can leverage this the next time you upgrade. 🙏
I did not actually find a good, concise and up-to-date-enough article / piece of documentation on how to switch to btrfs on a Raspberry Pi OS to link here. And I do not remember how I got there. 🙈 If you know of one, please let me know, I will gladly link to it! 🔗
To describe my setup more and why this makes sense …
I run a Raspberry Pi OS purely from a USB-connected SSD with a partition on btrfs with subvolumes for the OS, /home, other data and services.
Almost all services are run by Docker, so I can just switch the underlying OS subvolume, do a few docker compose up -ds and everything is set.
Pre-requisities
- rootfs as a subvolume on btrfs
/homeand other data directories in separate subvolumes as well
Doing the thing
Disclaimer: I have reconstructed this (from bash history and my mind) after I actually ran everything. Some commands need changed parameters/arguments based on your setup. Such as block device names. Use this as a template to guide you. Do not run commands you do not understand!
# get the image & uncompress
# here we have it at /root/2025-10-01-raspios-trixie-arm64-lite.img
# important to stop the "socket" first
systemctl stop docker.socket
systemctl stop docker.service
# we are gonna mount the main drive to /mnt/ssd
mkdir -p /mnt/ssd
mkdir -p /mnt/new-firmware
mkdir -p /mnt/new-rootfs
# make the img appear in /dev
losetup -Pf /root/2025-10-01-raspios-trixie-arm64-lite.img
mount /dev/sda2 /mnt/ssd
mount /dev/loop0p1 /mnt/new-firmware
mount /dev/loop0p2 /mnt/new-rootfs
btrfs subvolume create /mnt/ssd/@trixie
rsync --archive /mnt/new-rootfs/ /mnt/ssd/@trixie/
mkdir /boot/firmware/old
rsync --archive --exclude=/boot/firmware/old /boot/firmware/ /boot/firmware/old/
rsync --archive /mnt/new-firmware/ /boot/firmware/
# WARNING: Manually inspect there is nothing new in /boot/firmware/cmdline.txt
cp /boot/firmware/old/cmdline.txt /boot/firmware/cmdline.txt
# MANUALLY: edit subvolume in /boot/firmware/cmdline.txt to @trixie
cp /etc/fstab /mnt/ssd/@trixie/etc/fstab
# MANUALLY: edit /mnt/ssd/@trixie/etc/fstab subvolume to @trixie
echo "btrfs" >> /mnt/ssd/@trixie//etc/initramfs-tools/modules
# I do not remember which one it is now. 🙈
touch /boot/firmware/ssh
touch /boot/ssh
rsync --archive /root/.ssh/ /mnt/ssd/@trixie/root/.ssh/
# get rid of the annoying ssh banner
mv /mnt/ssd/@trixie/etc/ssh/sshd_config.d/rename_user.conf /mnt/ssd/@trixie/etc/ssh/sshd_config.d/rename_user.conf.disabled
reboot
# ssh to the new system, something like: ssh root@raspberrypi.lan
# ssh is gonna complain because of new server keys,
# that's okay, it's good to rotate them and a new OS version is a good opportunity,
# you need the delete the old keys from known_hosts for your pi
# just make sure no-one is doing a MITM attack
# On a new system:
apt update
apt list --upgradable
apt upgrade
apt install mc htop vim btrfs-progs docker.io docker-compose git wireguard-tools ufw
# disable ssh password auth
# setup wireguard / tailscale
# setup ufw
# do those docker compose up -d
If I get bored sometime, I will replace the manual steps with sed. Until then, use your favorite editor to do the changes. 🙏
In /boot/firmware/cmdline.txt you should have something like the following.
root@raspberrypi:~# cat /boot/firmware/cmdline.txt
console=serial0,115200 console=tty1 root=LABEL=rootfs rootflags=subvol=@trixie rootfstype=btrfs fsck.repair=no rootwait
Rolling back 🔃
If you want to roll back, just change the /boot/firmware contents back!
(that already contains the old cmdline.txt with the old rootfs subvolume name)
You can do that easily from another computer just by plugging in the drive and a few basic file operations. 🚀
Conclusion
That’s it!
This short writeup covers just one of the many benefits of using btrfs. Switching OSes is basically as easy on a Raspberry Pi as it is on a normal PC.
Maybe one day I will write about other btrfs features that I love and use … until then,
so long … and thanks for all the fish. 🐟🐟🐟
If you have comments, corrections or questions, feel free to send me an e-mail! 📧
🐬