< All posts

Feb. 12, 2021, 11:20 p.m.

Migrating Debian GNU/Linux EXT4 Filesystem To BTRFS with LUKS+LVM

Migrating Debian GNU/Linux EXT4 Filesystem To BTRFS with LUKS+LVM

From Arch Wiki, Btrfs is a modern copy on write (CoW) filesystem for Linux aimed at implementing advanced features while also focusing on fault tolerance, repair and easy administration..

Situation: You have full disk encrypted (LUKS) with LVM RootFS with unencrypted boot partition and you want to transfer it to a new Hard Disk Drive or Solid State Drive, the drive partition table is Master Boot Record (MBR) and you are using Debian Stable.
You do not want to install a new OS and reconfigure everything from scratch.

Note: The latest Debian Stable at the time of writing uses the Linux kernel 4.19. Swap can be used with btrfs with Kernel 5.1, so the LVM is not needed at all.

ATTENTION ATTENTION ATTENTION: PLEASE BACKUP YOUR VALUABLE DATA BEFORE ATTEMPTING TO DO THIS TUTORIAL! I WILL NOT BE RESPONSIBLE FOR THE LOSS OF YOUR DATA.

Installation of BTRFS tools

# apt update
# apt install btrfs-progs duperemover

Sample output:

# apt install btrfs-progs duperemove
Reading package lists... Done
Building dependency tree       
Reading state information... Done
btrfs-progs is already the newest version (4.20.1-2).
duperemove is already the newest version (0.11.1-3).

Partitioning

You can use gparted to make partitions easily.

The old partition layout:
Type: Master Boot Record (MBR)

Partition 1 - /boot (ext4) 1GB
Parition 2 - LUKS (LVM inside)

LVM name: OLD
OLD/root - / (root partition with EXT4 filesystem)
OLD/swap - swap partition


The new partition layout:
Type: Master Boot Record (MBR)

Partition 1 - /boot (ext4) 1GB
Partition 2 - LUKS (LVM inside, BTRFS doesn't support swap on <5.1 linux kernel (Debian stable is currently at 4.19)

LVM Name: NEW
NEW/btrfs - btrfs volume
NEW/swap - swap partition

Creating the filesystems

This assumes that your second disk is at /dev/sdb, the primary disk is at /dev/sda.

Make the /boot filesystem to be ext4: # mkfs.ext4 /dev/sda1

Making the LUKS partition:

# cryptsetup -c aes-xts-plain -s 256 luksFormat /dev/sdb2

WARNING!
========
This will overwrite data on /dev/sdb2 irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase: 
Verify passphrase: 

Open the LUKS partition

# cryptsetup luksOpen /dev/sdb2 NEW
Enter passphrase for /dev/sdb2: 

Create a new physical volume

# pvcreate /dev/mapper/sdb2
  Physical volume "/dev/mapper/sdb2" successfully created.

Display physical volumes:

# pvdisplay

Create volume group named NEW

# vgcreate NEW /dev/mapper/sdb2

Display volume group to show the usable space (Free PE)

# vgdisplay

   ...
  Free  PE / Size       25620 / 100.08 GiB

Create our logical volumes: (Encrypted SWAP Partition for btrfs)

At the time of writing the latest Debian 10 uses 4.19 kernel, so we need to setup LVM for our swap.

# lvcreate -L 4G -n swap NEW
  Logical volume "swap" created.

Show the logical volumes:

# vgdisplay

  ...
  Free  PE / Size       25108 / 98.08 GiB

The free space is now 98GB or 25108

Create the partition for our btrfs root:

# lvcreate -l 25108 -n rootfs NEW
  Logical volume "rootfs" created.

Setting up the Linux swap partition:

# mkswap  /dev/NEW/swap
Setting up swapspace version 1, size = 4 GiB
no label, UUID=9xx-xx-x

Setting up the BTRFS:

Create our top level btrfs root:

# mkfs.btrfs -L btrfs /dev/NEW/rootfs

Mounting the newly created btrfs:

# mount /dev/NEW/rootfs /mnt

Creating btrfs subvolume for home and /:

# btrfs subv cr /mnt/root

Create subvolume '/mnt/root'

# btrfs subv cr /mnt/home

Create subvolume '/mnt/home

Unmounting and Mounting with subvol:

# umount /mnt
# mount -o subvol=root,noatime,compress=lzo /dev/NEW/rootfs /mnt                                                                  
# mkdir /mnt/{boot,home}
# mount -o subvol=home,noatime,compress=lzo /dev/NEW/rootfs /mnt/home                                                             

compress=lzo tells btrfs to use Lzo compression algorithm.

We will do a rsync of a running system, this is not recommended but for testing purposes this works. Be sure to stop all essential services that does disk writes in the background like MariaDB server, Prometheus, InfluxDB and Grafana.

Assume,
sda = HDD Source (our currently running system)
sdb = HDD Destination

sdb1 = boot partition with ext4
sdb2 = physical volume for encryption with LVM root and swap partitions inside (VG name NEW)

Volume group name - logical volume name:
NEW-rootfs = the destination rootfs with btrfs
NEW-swap = the created swap area partition

The source system is running, so we just start the cloning process:

This will take a while depending on the number of files and the speed of you disk. Also, be sure to close all programs and stop running services that does disk writes. Alternatively, you can do this on a Live USB/CD which is much preferred.

# rsync  --progress  -avhPHAXx --exclude={/dev/*,/proc/*,/sys/*,/tmp/*,/run/*,/mnt/*,/media/*,/lost+found}  / /mnt
sending incremental file list
./
.autorelabel
.
.

sent 26.49G bytes  received 36.42M bytes  3.80M bytes/sec
total size is 49.82G  speedup is 1.80

The command above rsync’s the running system to the btrfs volume, excluded directories are dev, proc, run, sys, tmp, mnt, media and lost+found.

chrooting to the new btrfs system

We now need to chroot to the new btrfs drive, before doing that we need to note the UUIDs of each disks.

Display the blkids or just use GNOME Disk utility to determine the UUID of each partitions:

# blikd

/dev/sda1: UUID="source-boot" TYPE="ext4" PARTUUID=""
/dev/sda2: UUID="source-pv-luks" TYPE="crypto_LUKS" PARTUUID=""
/dev/mapper/OLD: UUID="bla" TYPE="LVM2_member"
/dev/mapper/OLD-swap: UUID="my-source-swap-UUID" TYPE="swap"
/dev/mapper/OLD-rootfss: LABEL="root" UUID="my-source-rootfs-UUID" TYPE="ext4"

/dev/sdb1: UUID="dest-boot" TYPE="ext4" PARTUUID=""
/dev/sdb2: UUID="dest-pv-luks" TYPE="crypto_LUKS" PARTUUID=""
/dev/mapper/NEW: UUID="bla2" TYPE="LVM2_member"
/dev/mapper/NEW-swap: UUID="my-dest-uuid-swap" TYPE="swap"
/dev/mapper/NEW-rootfs: UUID="my-dest-uuid-rootfs" TYPE="btrfs"

Chroot and mount the /boot partition inside the chroot environment.

# for i in /sys /proc /run /dev; do mount --bind "$i" "/mnt$i"; done
# chroot /media/user/root/
# mount /dev/sdb1 /boot

Edit the crypttab and replace the OLD to our NEW blkid and dm name:

# nano /etc/crypttab

OLD UUID=this-is-my-encrypted-luks-sda2-source none luks

TO

NEW UUID=this-is-my-encrypted-luks-sdb2-dest none luks

Whereas, OLD is the device mapper name from /dev/mapper/ directory, and NEW is also our cryptsetup luksOpen mapper name.

Edit the fstab (replace the old entries with our ‘destination’ entries):

# nano /etc/fstab

The old fstab entries:

UUID=source-boot /boot           ext4    defaults        0       2
UUID=my-source-swap-UUID         none     swap    sw      0       0
UUID=my-source-rootfs-UUID /     ext4    errors=remount-ro 0       1

The new fstab with btrfs:

UUID=my-dest-uuid-rootfs /               btrfs    rw,noatime,compress=lzo,space_cache,subvol=root   0    0
UUID=my-dest-uuid-rootfs /home           btrfs    rw,noatime,compress=lzo,space_cache,subvol=home   0    0
UUID=dest-boot  /boot          ext4     defaults,noatime        0       2
UUID=my-dest-uuid-swap none            swap     sw              0       0

Update the initramfs:

# update-initramfs -u -k all

[sudo] password for user: 
update-initramfs: Generating /boot/initrd.img-4.19.0-14-amd64
...
live-boot: core filesystems devices utils udev wget blockdev dns.

Install the GRUB bootloader to the Master Boot Record of the target drive, in our case /dev/sdb.

# grub-install /dev/sdb
Installing for i386-pc platform.

Update GRUB configuration:

# update-grub

Generating grub configuration file ...
Found background image: .background_cache.png
...
done

Exit Chroot and Unmount:

# for i in /sys /proc /run /dev; do umount "/mnt$i"; done
# umount /mnt/boot
# umount /mnt/home
# umount /mnt

PS. If you can’t unmount cleanly execute them with the -lf command (eg. umount -lf /path).

Close volume groups, LUKS and reboot to the new drive with btrfs.

# sync
# vgchange -an
# cryptsetup luksClose /dev/mapper/NEW
# reboot

This is tested on Master Boot Record, not on GPT.
If you cannot still boot, please double check the crypttab entry and the /dev/mapper name.

References and interesting read:

Rsync command stackoverflow
https://btrfs.wiki.kernel.org/index.php/Getting_started
https://wiki.archlinux.org/index.php/Btrfs
https://www.paritybit.ca/blog/debian-with-btrfs
https://fogelholk.io/installing-arch-with-lvm-on-luks-and-btrfs/
https://gist.github.com/urwx/738e40217c4c7fd4ac896c7a4b71ba9e

Except where otherwise noted, this work is licensed under Creative Commons Attribution-ShareAlike 4.0 International License (http://creativecommons.org/licenses/by-sa/4.0/).
I hope that this post is useful to you, if you liked this post you may support me via Patreon or liberapay. Thank you for your support.