Btrfs
This is a procedure to convert a root filesystem into btrfs for a server, either on a Gandi VM or a Scaleway machine.
This was written after a number of trial-error. Here are my steps if it can be useful to others. For an introduction, see Wikipedia article and/or the dedicated wiki.
WARNING: this could cause DATA LOSS, be sure you have backups (possibly Gandi snapshots).
General strategies
Often the VM are formatted in ext4 by default without option to easily use another filesystem. To hackily change this, there are 2 slightly different strategies:
- detach the root filesystem and attach it to another machine, convert it, and re-attach it to the original machine,
- use a secondary system to convert the filesystem with btrfs-convert, when it is not possible to detach the root filesystem (e.g. OpenStack).
GandiCloudVPS
Tested on Debian 12 on 2024-10-08.
GandiCloudVPS is powered by OpenStack, and it is not possible (according to my understanding) to detach the root filesystem, so it is not possible to mount another volume (although a new server may be created with an existing volume with openstack server create --volume UUID …
).
- Restart the VM in rescue mode
- Connect by SSH or with the remote console in this rescue mode
- Identify the filesystems on the server in rescue mode with
blkid
and/ordf -hT
, for instance/dev/xvdb1
for the root filesystem/
/dev/xvdb15
for/boot/efi
apt-get update && apt-get install -y btrfs-progs
btrfs-convert -p /dev/xvdb1
mount /dev/xvdb1 /mnt
btrfs subvolume delete -C /mnt/ext2_saved
btrfs filesystem defragment -r /mnt
btrfs balance start /mnt
blkid
and note the UUID of the btrfs filesystemcp /mnt/etc/fstab /tmp
(to temporarily keep a backup if needed)- Edit
/mnt/etc/fstab
, replace the UUID of the root filesystem by the new UUID, replace the type "ext4" by "btrfs", and replace the options by "defaults" for i in dev dev/pts proc sys; do mount --bind /$i /mnt/$i; done
mount /dev/xvdb15 /mnt/boot/efi
chroot /mnt update-grub
chroot /mnt grub-install /dev/xvdb
for i in dev/pts dev proc sys boot/efi boot ""; do umount /mnt/$i; done
- Restart in normal mode
- Verify that everything works with
df -hT
- If the server does not restart, it may be observed why on the remote console. Then restart in rescue mode, remount the new filesystem on
/mnt
and check:- that
/mnt/etc/fstab
has the right UUID, type "btrfs", options "defaults"
- that
Gandi CloudV5
This was updated on 2020-09-24 for Debian 10 buster + GandiV5, and on 2022-02-02 for Debian 11 bullseye + GandiV5. An older version was for Debian 9 jessie or Ubuntu 18.04 LTS + GandiV4.
Manual procedure
Create the server
On GandiV5, create a new server with e.g. 1 proc, 256 MiB RAM, 1 system disk of 5 GiB, Debian 10 buster or Debian 11 bullseye.
For Debian 10 buster (only), define backports (btrfs-progs must contain btrfs-convert, see this Debian bug) (this is not needed for Debian 11):
echo 'deb https://deb.debian.org/debian buster-backports main' > /etc/apt/sources.list.d/debian-backports.list
Install btrfs-progs:
apt update && apt install -t buster-backports btrfs-progs # Debian 10 buster apt update && apt install btrfs-progs # Debian 11 bullseye
On GandiV5 :
- stop the server,
- open the page about the system disk in Volumes,
- clone the system disk with another (definitive) name (e.g. the current disk is "sys-myserver", you can define the copied disk as "myserver"),
- start the server,
- wait until the server is started,
- attach this cloned disk to the server.
ATTENTION: really start the server before attaching the cloned disk, else the root filesystem might be the cloned disk given they have the same UUID and /etc/fstab is using the UUID to select the root filesystem (it could be workarounded by setting /dev/xvda1 in /etc/fstab before stopin the server).
Convert to btrfs
- Mostly from [1]
Display the active root filesystem, and you will be able to deduce what is the cloned filesystem:
ls /dev/xvd* mount|grep xvd blkid
Copy the result of blkid
in some text file on your computer (will be used later).
Let’s say /dev/xvdb1 is the clone filesystem, we convert it to btrfs: (man 8 btrfs-convert)
fsck.ext4 -f /dev/xvdb1 # optional btrfs-convert -p /dev/xvdb1
Here, I obtained the following error:
root@test:~# btrfs-convert -p /dev/xvdb1 create btrfs filesystem: blocksize: 4096 nodesize: 16384 features: extref, skinny-metadata (default) creating ext2 image file creating btrfs metadata Unable to find block group for 0 27081] Unable to find block group for 0 Unable to find block group for 0 ctree.c:2245: split_leaf: BUG_ON `1` triggered, value 1 btrfs-convert(+0x11b5a)[0x559c159c1b5a] btrfs-convert(+0x1589b)[0x559c159c589b] btrfs-convert(btrfs_search_slot+0x269)[0x559c159c6401] btrfs-convert(btrfs_insert_empty_items+0x92)[0x559c159c7b3c] btrfs-convert(btrfs_record_file_extent+0x1bc)[0x559c159d46b4] btrfs-convert(record_file_blocks+0x14a)[0x559c159bfe92] btrfs-convert(+0x10349)[0x559c159c0349] btrfs-convert(+0x1135f)[0x559c159c135f] btrfs-convert(main+0x1f59)[0x559c159bdefb] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x7f4c11c3bb97] btrfs-convert(_start+0x2a)[0x559c159bb5ca] Aborted
According to [2] and [3], it can be worked around: (this command could take some hours, depending on disk size and number of files)
btrfs-convert -d -p /dev/xvdb1
or even:
btrfs-convert -n -d -p /dev/xvdb1
Or, if still unsuccessful, try to add free space, or if still unsuccessfull, re-compile btrfs-progs in version 4.17.1 (this last try worked for me for 50 Gio disk with 24 Gio free space on Debian 9 jessie, expanded after its original size was 30 Gio (=4 Gio free space)).
Then, mount the filesystem and delete the old ext4 snapshot:
mount /dev/xvdb1 /mnt btrfs subvolume delete /mnt/ext2_saved btrfs filesystem defrag -r /mnt # could take dozen of minuts btrfs balance start /mnt # could take hours
Promote as root filesystem
- Mostly from [4]
Edit the /etc/fstab of the btrfs system:
uuid=`blkid|grep -P '^/dev/xvdb1: '|grep -o -P ' UUID="[0-9a-f-]+" '|cut -d'"' -f2` sed -i -E 's/^UUID=[0-9a-f-]+ *\/ .*$/UUID=$uid \/ btrfs defaults 0 1/' /mnt/etc/fstab
Update Grub in a chrooted system:
for i in dev dev/pts proc sys; do mount --bind /$i /mnt/$i; done chroot /mnt update-grub for i in dev/pts dev proc sys; do umount /mnt/$i; done umount /mnt
Quit:
exit
Define the cooked disk as boot disk
In Gandi V5 interface, stop the server, detach the old root disk and define the cooked disk as "Use to start".
Launch the server (it should correctly start, even if the /boot directory is on the main partition / (some old documents said it didn’t work because grub didn’t know the btrfs filesystem, but it is fixed now)).
df -hT
It should show something like:
/dev/xvda1 btrfs 50G 32G 19G 63% /
Delete the old root disk.
You can delete the packages grub-efi-amd64, grub-efi-amd64-bin, grub-pc, grub-pc-bin since they are not used, but keep /etc/default/grub with Gandi customisations:
cp -a /etc/default/grub /root/grub apt-get purge -y grub-efi-amd64 grub-efi-amd64-bin grub-pc grub-pc-bin mv /root/grub /etc/default/grub apt-mark manual grub-common grub2-common update-grub # to check it still work and you should reboot now to be sure it reboots correctly
Scripted procedure
This script automates the conversion, with a slightly-modified procedure.
On GandiV5, create a new server with e.g. 1 proc, 256 MiB RAM, 1 system disk of 5 GiB, Debian 11 bullseye.
On GandiV5 :
- stop the server,
- open the page about the system disk in Volumes,
- clone the system disk with another (definitive) name (e.g. the current disk is "sys-myserver", you can define the copied disk as "myserver"),
- start the server,
- wait until the server is started,
- attach this cloned disk to the server.
ATTENTION: really start the server before attaching the cloned disk, else the root filesystem might be the cloned disk given they have the same UUID and /etc/fstab is using the UUID to select the root filesystem (it could be workarounded by setting /dev/xvda1 in /etc/fstab before stopin the server).
- Launch this script as root: (
vi convert_gandiroot_to_btrfs.sh
+chmod +x convert_gandiroot_to_btrfs.sh
+ (./convert_gandiroot_to_btrfs.sh
)- The script takes about 3 minutes to convert a 1.4 GiB disk.
#!/bin/sh
set -e
fs_type=`mount|grep -F ' on / '|grep -o -E ' type [a-z0-9]+ '|cut -d' ' -f3`
blkid=`blkid|grep -F ' LABEL="gandiroot" '`
nb_fs_gandiroot=`echo "$blkid"|cut -d: -f2-|uniq -c|tr -s ' '|cut -d' ' -f2`
if [ "$fs_type" != 'ext4' -o ! "$nb_fs_gandiroot" = 2 ]; then
echo 'It is not possible to convert the disk. There should be two identical devices labeled "gandiroot" formatted in ext4'
exit 2
fi
fs_gandiroot_ext4=`echo "$blkid"|head -n1|cut -d: -f1`
fs_gandiroot_btrfs=`echo "$blkid"|tail -n1|cut -d: -f1`
echo
echo "Current root filesystem: $fs_gandiroot_ext4 (ext4)"
echo "Future root filesystem: $fs_gandiroot_btrfs (currently ext4, will be btrfs)"
set -v
apt-get update
apt-get install -y btrfs-progs
fsck.ext4 -f "$fs_gandiroot_btrfs"
btrfs-convert -p "$fs_gandiroot_btrfs"
mount "$fs_gandiroot_btrfs" /mnt
btrfs subvolume delete -C /mnt/ext2_saved
btrfs filesystem defragment -r /mnt
btrfs balance start /mnt
new_uuid=`blkid|grep -P "^$fs_gandiroot_btrfs: "|grep -o -P ' UUID="[0-9a-f-]+" '|cut -d'"' -f2`
sed -i -E "s/^UUID=[0-9a-f-]+ *\/ .*$/UUID=$new_uuid \/ btrfs defaults 0 1/" /mnt/etc/fstab
for i in dev dev/pts proc sys; do mount --bind /$i /mnt/$i; done
chroot /mnt update-grub
for i in dev/pts dev proc sys; do umount /mnt/$i; done
umount /mnt
In Gandi V5 interface, stop the server, detach the old root disk and define the cooked disk as "Use to start".
Launch the server and execute:
df -hT
It should show something like:
/dev/xvda1 btrfs 5G 1.4G 3.5G 28% /
Delete the old root disk.
Scaleway Elastic Metal
Tested on Debian 11.
- On the running server, find the name of the partitions with
blkid
for the root filesystem/
and/boot
and/boot/efi
- Restart in rescue mode
- Identify the filesystems on the server in rescue mode with
blkid
and/ordf -hT
, for instance/dev/md126
for the root filesystem/
/dev/md127
for/boot
/dev/sda1
for/boot/efi
apt-get update && apt-get install -y btrfs-progs
btrfs-convert -p /dev/md126
mount /dev/md126 /mnt
btrfs subvolume delete -C /mnt/ext2_saved
btrfs filesystem defragment -r /mnt
btrfs balance start /mnt
blkid
and note the UUID of the btrfs filesystemcp /mnt/etc/fstab /tmp
(to temporarily keep a backup if needed)- Edit
/mnt/etc/fstab
, replace the UUID of the root filesystem by the new UUID, replace the type "ext4" by "btrfs", and replace the options by "defaults" for i in dev dev/pts proc sys; do mount --bind /$i /mnt/$i; done
mount /dev/md127 /mnt/boot
mount /dev/sda1 /mnt/boot/efi
chroot /mnt update-grub
for i in dev/pts dev proc sys boot/efi boot ""; do umount /mnt/$i; done
- Restart in normal mode
- Verify that everything works with
df -hT
- If the server does not restart, restart in rescue mode, remount the new filesystem on
/mnt
and check:- that
/mnt/etc/fstab
has the right UUID, type "btrfs", options "defaults" - perhaps
chroot /mnt grub-install /dev/md126
or something like that may be needed, after the six filesystems /mnt/dev… were mounted
- that
Troubleshootings
- During a conversion, the root filesystem was mounted read-only in btrfs; it was probably because I didn’t change the options "rw,noatime,errors=remount-ro" to "defaults"