Create btrfs subvolumes retrospectively

This posts describes how to create subvolumes in btrfs retrospectively (with existing data).

Iā€™m using archlinux, but most of the things should be applicable to other distors as well.
It is mostly a list of the required commands, with a tiny bit of explanations :).
All the commands are real world examples from my system, so you need to adapt device names etc. to your setup.

Move the top-level volume to a subvolume

  • create a snapshot
    1
    sudo btrfs subvol snapshot / /@

(ā€œ@ā€ seems to be a common name for the main subvolume)

  • set the new snapshot as default subvolume

    1
    2
    3
    4
    sudo btrfs subvol list /
    # outputs: ID 330 gen 1354 top level 5 path @
    # use the id from the output above to
    sudo btrfs subvol set-default 330 /
  • if it is not the root volume, skip to the last point of this section

  • adapt grub to boot from the new subvolume

    • mount the new subvolume

      1
      2
      3
      cd /mnt
      sudo mkdir newroot
      sudo mount -o subvol=@ /dev/mapper/cryptroot newroot
    • edit newroot/etc/fstab to include subvol=@ in the / options
      This should not be needed theoretically since it is the default volume, but grub-mkconfig did not produce a correct config for me without it.

      1
      2
      UUID=2240f8f3-3dfa-4aad-a62f-899f5970cb3d / btrfs defaults,subvol=@,noatime,discard,space_cache,autodefrag,compress=lzo
      0 0
    • chroot into the new subvolume and update grub

      1
      2
      3
      4
      5
      6
      7
      for i in /dev /dev/pts /proc /sys /run; do sudo mount --bind $i newroot$i; done
      sudo mount /dev/mapper/cryptboot newroot/boot
      sudo mount /dev/sdc1 newroot/boot/efi
      sudo mount --bind /sys/firmware/efi/efivars newroot/sys/firmware/efi/efivars
      sudo chroot /mnt/newroot
      grub-mkconfig -o /boot/grub/grub.cfg
      grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=arch-grub --recheck
    • unmount and reboot (at your own risk of course :D)

  • delete the old data from the toplevel

    1
    2
    3
    4
    5
    cd /mnt
    sudo mkdir oldroot
    sudo mount -o subvolid=5 /dev/mapper/cryptroot oldroot
    cd oldroot
    # CAREFULLY delete everything except @ subvolume


Move existing directory to a subvolume

In this example the directory /home/martin/develop will be replaced with a subvolume.

  • create the new subvolume

    1
    2
    3
    sodo mkdir /mnt/toplevel
    sudo mount -o subvolid=5 /dev/mapper/cryptroot /mnt/toplevel
    sudo btrfs subvol create /mnt/toplevel/@develop
  • move the data to the new subvolume in the most efficient way (as far as I know) by creating reflinks

    1
    sudo cp -ax --reflink=always /mnt/toplevel/@/home/martin/develop/. /mnt/toplevel/@develop
  • delete the old directory

    1
    2
    # DO THIS AT YOUR OWN RISK
    rm -rf /home/martin/develop
  • mount the new subvolume instead

    1
    2
    mkdir /home/martin/develop
    sudo mount -o subvol=@develop /dev/mapper/cryptroot /home/martin/develop
  • add new fstab entry to mount the new subvolume automatically

    1
    2
    UUID=2240f8f3-3dfa-4aad-a62f-899f5970cb3d /home/martin/develop btrfs defaults,subvol=@develop,noatime,discard,space_cac
    he,autodefrag,compress=lzo 0 0

Note that the top-level volume always has the ID 5.

Reference: https://btrfs.wiki.kernel.org/index.php/UseCases#Can_I_take_a_snapshot_of_a_directory.3F


Thanks for reading, I hope it was helpful to you :)