Migrate a Fedora Silverblue install to a new SSD

I wanted to upgrade my laptop hard-drive to double the capacity. However it only has a single M.2 slot and I wanted as little downtime as possible. So moving ‘in-place’ was not possible. Fortunately I have a desktop PC with a free M.2 slot that could take the new drive. Since my laptop is running Fedora Silverblue on a btrfs file system I was hoping to use btrfs send/receive to move subvolumes to the new drive. Here is how I proceeded (only broad strokes and from memory, I might have left out some steps).

Aside:

I started the laptop using Fedora Silverblue 35. Starting in version 36 the default installer creates a dedicated subvolume for /var. So I used this opportunity to make the same change by copying the contents of var from the root snapshot (see below) to a newly created new_var subvolume. For this:

sudo mount /dev/mapper/luks-.... /mnt
sudo btrfs subvolume create /mnt/new_var
cp -a --reflink=auto /var/snapshots/root-ro/ostree/deploy/fedora/deploy/commit_hash/var/* /mnt/new_var

If your install already has a dedicated var subvolume, you need to snapshot that one instead of the root subvolume. I try to indicate this below.

On the desktop:

  1. install a fresh Silverblue install, making sure to configure the same users as on the laptop (to have the same uids)
  2. bring the state to the same commit as the laptop (and layer the same packages on top). This step is only possible on Silverblue and part of what makes it so amazing. You get the commit hash running rpm-ostree status on the laptop then deploy it with sudo rpm-ostree deploy commit_hash on the desktop
  3. receive the btrfs subvolumes:
nc 14000 | sudo btrfs receive -ve /var/snapshots

On the laptop:

  1. take a read-only snapshot of the home subvolume and send it to the desktop using netcat
sudo btrfs subvolume snapshot -r /home /var/snapshots/home-ro
sudo btrfs send -v /var/snapshots/home-ro | nc desktop_ip 14000
  1. take a read-only snapshot of the root volume. For this mount the root filesystem to a mount point first:
sudo mount /dev/mapper/luks-... /mnt
sudo btrfs subvolume snapshot -r /mnt/root /var/snapshots/root-ro
sudo btrfs send -v /var/snapshots/root-ro

If you are on Fedora Silverblue 36 or above and already have a separate var subvolume please only snapshot and transfer that one by replacing /mnt/root with /mnt/var in the snapshot command above.

Back on the desktop

Now we just need to create read-write snapshots of the sent volumes and change fstab to mount these instead:

sudo btrfs subvolume snapshot /var/snapshots/home-ro /mnt/new_home
#repeat with var-ro to /mnt/new_var or using the cp command in the 'Aside:'
section to create at new_var subvolume

Then edit /etc/fstab and change the home mountpoint to use subvol=new_home and var to subvol=new_var. It should look like this

UUID=... /     btrfs   subvol=root,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=... /home btrfs   subvol=new_home,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=... /var  btrfs   subvol=new_var,compress=zstd:1,x-systemd.device-timeout=0 0 0

The root subvolume stays the same, the other two mountpoints refer to the subvolumes copied over from the laptop. Now reboot and confirm you have access to all previous data. Then swap out the old drive for the new drive and boot into the laptop to enjoy twice the amount of space than before.

After this you can cleanup the subvolumes by deleting the var and home subvolumes create by the installer. After that one could also rename new_var to var and new_home to home.

Conclusion and Outlook

Now your system is at the same state in terms of packages and kernel and all state in /var is conserved. However we lost all state in /etc somehow. That includes wifi passwords, systemd services, groups and probably more. I could not fully figure out how /etc is populated and what would be a safe way to migrate. However this method has led to almost no downtime. In my case, I just had to fix some of my user groups and copy over a few systemd service from the snapshot post migration. So it makes sense to keep the snapshots around for a while.

In the future, I really want to move my system to a fully declarative one. I’m looking at Guix System, however in my last tests it was not stable enough and I could not be friend with the Shepherd as PID 1 instead of systemd. NixOS has a weird configuration language that I do not want to learn. I feel Fedora Silverblue already brings atomic updates with rollback (which saved my ass when I did mistakes updating fstab in the steps above) to the table. Combined with guix as package manager for userspace (using guix home) I can see this being a viable option.

Main blocker for now is to create a silverblue image/spin that contains a working guix install, which is difficult because guix uses /gnu/store whereas / is immutable in Silverblue. Also this would not solve the issues with creating users and groups in a reproducible way. However reading more of the (rpm-)ostree documentation it seems that these are handled by systemd-sysusers and can be declared up front. Once I figure it all out, I will post about it here.