Make a backup first

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-progsEndeavourOS, 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.

About defragmentation

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.