btrfs-convert is broadly reliable, but "in-place filesystem conversion" is a sentence with a non-zero failure rate. Take a full disk image (dd if=/dev/sda of=/external/backup.img bs=4M status=progress) before you start. If you don't have an external disk big enough, you don't have any business doing this conversion — do a clean install instead.
Boot from a live ISO
You can't convert a mounted root partition. Boot from any live distro that has btrfs-progs — EndeavourOS, the Arch installer ISO, Manjaro live, or Fedora live all work.
Once booted, become root:
sudo -i
Identify the partitions
fdisk -l
For example, suppose you have:
/dev/sda1— EFI System Partition (FAT32, ~500 MB)/dev/sda2— swap (optional)/dev/sda3— root (ext4)/dev/sda4— home (ext4)
Adjust the rest of this article to match your layout.
Check the source filesystems
fsck -f /dev/sda3
fsck -f /dev/sda4
The conversion absolutely requires a clean filesystem. -f forces a check even if the journal says everything is clean. If fsck reports errors, fix them first — do not attempt the conversion on a damaged ext4 filesystem.
Convert
btrfs-convert /dev/sda3
btrfs-convert /dev/sda4
This can take a while — expect a couple of minutes per gigabyte on spinning rust, much less on SSD. btrfs-convert writes the new Btrfs metadata into the free space of the existing ext4 filesystem, then leaves the original ext4 image as a Btrfs subvolume named ext2_saved. That subvolume is your one-shot rollback path: btrfs-convert -r /dev/sda3 turns it back into ext4 if something has gone wrong, but only as long as you haven't deleted that subvolume.
Mount the converted filesystem and chroot in
mount /dev/sda3 /mnt
mount /dev/sda4 /mnt/home
# Mount the EFI partition
mount /dev/sda1 /mnt/boot
# (or /mnt/efi or /mnt/boot/efi depending on where your distro mounts it)
# Bind-mount the live system's special filesystems
mount -t proc none /mnt/proc
mount -t sysfs none /mnt/sys
mount --rbind /dev /mnt/dev
mount --rbind /run /mnt/run
# For UEFI variables (needed for grub-install on UEFI)
modprobe efivarfs
mount -t efivarfs efivarfs /sys/firmware/efi/efivars
# Enter the chroot
chroot /mnt /bin/bash
Update fstab with new UUIDs
Get the new Btrfs UUIDs:
blkid /dev/sda3
blkid /dev/sda4
# Note the UUID="..." values
Edit /etc/fstab:
nano /etc/fstab
Replace the old UUIDs with the new ones, and change the filesystem type from ext4 to btrfs. While you're there, switch to recommended mount options:
# /dev/sda3 (root)
UUID=NEW-ROOT-UUID / btrfs rw,noatime,compress=zstd:3,space_cache=v2 0 0
# /dev/sda4 (home)
UUID=NEW-HOME-UUID /home btrfs rw,noatime,compress=zstd:3,space_cache=v2 0 0
# /dev/sda1 (EFI) — unchanged
UUID=EFI-UUID /boot vfat defaults 0 2
# /dev/sda2 (swap) — unchanged
UUID=SWAP-UUID none swap defaults 0 0
The compress=zstd:3 option transparently compresses everything that benefits from it (text, source code, package archives) at compression level 3. The savings on a typical Linux install are 20–30%; the CPU cost on a modern CPU is negligible.
Rebuild GRUB
The GRUB bootloader has the old root UUID hard-coded in its config file. Regenerate it:
os-prober # have grub-mkconfig pick up other OSes
grub-mkconfig -o /boot/grub/grub.cfg
# Reinstall the GRUB binary (UEFI):
grub-install --efi-directory=/boot --target=x86_64-efi /dev/sda
# Or for legacy BIOS:
# grub-install --target=i386-pc /dev/sda
Rebuild initramfs
The initramfs needs to know about Btrfs to mount the root filesystem. On Arch:
nano /etc/mkinitcpio.conf
# Make sure 'btrfs' is in the MODULES=() line, OR
# 'btrfs' is in the HOOKS=() line (preferred — autoloads only what's needed)
mkinitcpio -P
On Debian/Ubuntu:
update-initramfs -u -k all
Exit the chroot, unmount everything, reboot:
exit
umount -R /mnt
reboot
Post-reboot housekeeping
If everything boots cleanly, reclaim the space the original ext4 was using:
btrfs subvolume delete /ext2_saved
Once you delete this subvolume, btrfs-convert -r can no longer roll back. Don't run it until you're confident the new filesystem is healthy.
Snapshots: manual
The simplest snapshot workflow is by hand:
btrfs subvolume snapshot / /snapshots/before-upgrade
# Now: dnf upgrade / pacman -Syu / apt full-upgrade
# Roll back if needed:
btrfs subvolume set-default /snapshots/before-upgrade /
reboot
Snapshots: automated with Snapper
Snapper integrates with the package manager (via snap-pac on Arch / dnf-plugin-snapper on Fedora) to take a snapshot before and after every system update.
# On Arch via the AUR:
yay -S snapper snap-pac snapper-gui
sudo snapper -c root create-config /
sudo snapper -c home create-config /home
sudo snapper list-configs
sudo systemctl enable --now snapper-timeline.timer snapper-cleanup.timer
From here, every pacman -Syu creates a labeled snapshot you can roll back to. The snapshots are copy-on-write — they cost essentially nothing in disk space until something changes.
Btrfs is a CoW filesystem; under heavy random-write workloads (databases, virtual machine images) it fragments. Mark those files with chattr +C to disable CoW per-file (you lose snapshots for those files but eliminate the write amplification), or run btrfs filesystem defragment periodically.