Scenario

In my first BTRFS post I described most of its basic features and focused on creating and maintaining snapshots. After using BTRFS for a time, a time came to replace existing 120GB SSD drive with a larger one. My laptop has one M2 factor SSD disk and another 2’’ hard drive. The new M2 SSD is 250GB but I cannot have both SSD in the hardware since there is only one M2 slot.

I made a few bad decisions before I came to a proper procedure:

1st

Initially I thought the good enough way would be to release some space at hard drive, create a partition there to hold a copy of the SSD disk. Then add this partition as another device as a mirror to the existing BTRFS volume. Then split the mirror, remove the old 120GB SSD and leave the system booting from the disk drive mirror. Then add 250G SSD, replacing the old 120GB one. It sounded like a good plan. But don’t do this at home kids. That is large risk when you do it first time. And for a while I thought I was left without any system left. Yes, of course, I did not take a backup.

Instead of failing a drive I converted the RAID1 to a single profile device (differs from RAID0). Luckily I was able to restore the system when I inserted the original disk back to it. Don’t go this way.

2nd

This time at least I started with backup. I knew about btrfs-send and btrfs-received but only a little. So I started making the backup with tar. It always works but is not a great tool for a filesystem dump like btrfs. Anyway, at least it is better than nothing.

Reading about moving data from one BTRFS to another I stumbled on these send/receive commands to do the job. They seemed to me to binary copy BTRFS snapshots pretty well. So I adjusted the 1st procedure to include now the send/receive commands.

It did the trick actually. I copied data from the 120GB SSD to the hard disk partition. Swaped 120/250GB SSD disks a few times before I decided I am satisfied with the system on the hard drive and finally booted without the original 120GB SSD. Then I repeated the whole procedure again to move the daa from the hard drive to the larger SSD.

I forgot about a few things. I forgot to copy subvolumes I created to exclude snapper snapshosts. This is why I had to reinsert the old SSD and copy the missing stuff.

There was no 3rd scenario. Actually this one worked but was not the best optimal one. The best optimal would be to copy all stuff to an external usb drive using btrfs-send, replace the disks, boot from an independent Linux on an usb drive, and using btrfs-receive rebuild the system on the 250GB SSD. Next time maybe.

Configuration

My laptop has a hard disk drive of 320GB and another M2 SSD which is only 120GB in size. Benefits of SSD are so huge that actually I work only with the SSD. The hard disk is currently used only to store archive data and things I couldn’t affort keeping on SSD.

SSD (sdb) and hard disk (sda) layouts are as below:

name size description
sdb1 4G /boot, bootable partition
sdb2 99G / + /home
sdb3 8G swap
sda1 8G /boot copy from sdb1
sda4 130G new copy from sdb2

Now let’s inspect subvolume configuration.

btrfs subvolums list /
ID 257 gen 292008 top level 5 path @
ID 258 gen 292004 top level 5 path @home
ID 274 gen 291494 top level 258 path @home/jxa/.virtualbox
ID 275 gen 291511 top level 258 path @home/jxa/Downloads
ID 276 gen 291511 top level 258 path @home/jxa/tmp
ID 277 gen 291184 top level 258 path @home/jxa/VirtualBox VMs
ID 312 gen 292007 top level 257 path var/log
ID 358 gen 292005 top level 258 path @home/jxa/.cache
ID 359 gen 291559 top level 258 path @home/jxa/Arch
ID 437 gen 291998 top level 258 path @home/.snapshots
ID 929 gen 291370 top level 437 path @home/.snapshots/279/snapshot
ID 1761 gen 291551 top level 257 path .snapshot
[...]

There are also multiple snapshots created by snapper which I don’t care for and some coming from docker. Those docker were apparently used by some images I kept. Once the images were deleted, the corresponding snapshots disappeared automatically.

Safety Backup

The BTRFS send/receive commands work on readonly snaphots only. So I had to create the snapshots first:

btrfs subvolume snapshot -r / /root_ro 
btrfs subvolume snapshot -r /home /home/home_ro

Now lets create a safety backup of these volumes.

# btrfs send /root_ro | pv -terba | gzip > /media/jxa/backup/t530-btrfs-root.gz
# btrfs send /home/home_ro | pv -terba | gzip > /media/jxa/backup/t530-btrfs-home.gz

Restore to a New Disk

I am going to clone my system to the hard disk, the target partition will be /dev/sda4. I start with creating btrfs filesystem and mounting do /mnt/backup:

# mkfs.btrfs -d single -L btrfs2 -f /dev/sda4
# mount /dev/sda4 /mnt/backup
# mount | grep btrfs
/dev/sdb2 on / type btrfs (rw,realtime,space_cache,subvolid=257,subvol=/@)
/dev/sdb2 on /home type btrfs (rw,realtime,space_cache,subvolid=258,subvol=/@home)
/dev/sdb2 on /var/lib/docker/plugins type btrfs
             (rw,realtime,space_cache,subvolid=257,subvol=/@/var/lib/docker/plugins)
/dev/sda4 on /mnt/backup type btrfs (rw,realtime,space_cache,subvolid=5,subvol=/)

Docker mountpoint disappears once the docker service is stopped. It does not seem to be any other docker subvolume present once I have deleted all docker images.

Now lets clone root and home subvolumes to target disk:

# btrfs send /home/home_ro | pv -terba | btrfs receive /mnt/backup
At subvol /home/home_ro
At subvol home_ro
26,3GiB 0:12:16 [36,6MiB/s] [36,6MiB/s] 
# btrfs send /root_ro | pv -terba | btrfs receive /mnt/backup
At subvol /root_ro
At subvol root_ro
12,4GiB 0:07:30 [28,2MiB/s] [28,2MiB/s]

pv is a tool to progress monitoring while copying data through a pipe.

Now rename newly created subvolumes to more convenient names. It can be done simply through usual mv command:

# cd /mnt/backup
# mv home_ro @home
# mv root_ro @

Subvolumes above were created as read-only snapshots. I am checking their status and converting to read-write subvolumes:

# btrfs property get -ts /mnt/backup/@
ro=true
# btrfs property get -ts /mnt/backup/@home
ro=true
# btrfs property set -ts /mnt/backup/@ ro false
# btrfs property set -ts /mnt/backup/@home ro false

I also need to copy /boot from sdb1 to sda1 - so I just created a new ext2 filesystem at sda1, then created a copy of /boot (currently mounted from sdb1) to sdb1 using tar (or cp -p).

Preparing the New System

At this poing I have the full copy of the system. The only missing part is to configure the new /etc/fstab and reinstall GRUB.

# cd /mnt
# umount /mnt/backup
# mkdir root
# mount -t btrfs -o subvol=@ /dev/sda4 /mnt/root
# mount /dev/sda1 /mnt/root/boot
# for i in dev dev/pts sys proc run; do mount --bind /$i /mnt/root/$i; done
# chroot /mnt/root

Now at /mnt/root I have the new system, time to edit /etc/fstab adjusting device names to the new SSD disk. Once done, the only thing to do is to reinstall GRUB with this commands:

# update-grub
# grub-install --recheck /dev/sda
# exit

Umount, reboot and see if it works:

# cd /
# umount -R /mnt/root
# reboot

Boot Issues

The partition table at the 250GB appeared to be GPT type which I did not discovered until all was copied and ready. GRUB cannot install on the disk like that until it has a dedicated BIOS Boot Partition. I addressed the problem by dropping the sdb1 partition and creating two of them instead. One for the BIOS boot and another - reduced in size - for the original purpose. Luckily it was the /boot so it was easily reconstructed.

Resources

  1. BTRFS wiki.kernel.org - primary source of information on BTRFS.
  2. How to manage BTRFS Storage Pools, Subvolumes and Snapshots - RedHat documentation on LVM CLI.
  3. Incremental Backup
  4. Subvolumes and Snapshots - an LWN article.
  5. Snappper - automatic snapshots.
  6. Tuning Snapper - ArchLinux Wiki.
  7. ArchLinux on BTRFS
  8. BTRFS in Oracle Enterprise Liinux
  9. BTRFS features, Oracle’s summary.
  10. Fixing BTRFS full problems
  11. Moving BTRFS subvolume to another disk
  12. Installing GRUB on GPT
  13. Przywracanie GRUB z chroot (PL)
  14. Install GRUB from chroot
  15. Grub boot problems
  16. Ubuntu Community BTRFS