The ext4 filesystem -- and its preceding versions, known for its stability and reliability has long been the default filesystem used by most Linux distributions. However, the Btrfs filesystem, due to its scalability and next-generation fault tolerance and self healing features have helped it gain popularity.
openSUSE pioneered its use as the default filesystem almost ten years ago, provisioning it on installations with a complex but very rational subvolume layout, along with Snapper to manage snapshots and rollbacks. Since then maintenance services have increased its reliability and suitability for use as the default filesystem. Recently, an increasing number of distributions are using it as the default filesystem for installations, most notably Fedora, although the Btrfs subvolume layouts these distributions are far simpler than openSUSE's.
An existing ext4 installation can easily be converted to Btrfs with the btrfs-convert command, and with a little more effort, the converted installation can be configured to use the openSUSE subvolume layout. This article describes a process to convert an existing Ubuntu installation on ext4 to Btrfs with the openSUSE style subvolume layout and integrate Snapper for managing snapshots and rollbacks, with the ability to automatically create snapshots periodically, during each boot, and before and after package management transactions.
The Btrfsfilesystem is believed by some to be the next default filesystem for Linux, replacing the current de-facto standard filesystem, ext4, used by default in most distributions. It has many advanced features which are not available in the current standard filesystem, such as copy-on-write capability which maintains a version of a file at each modification. This allows individual files to be rolled back to previous versions and even the entire system to be snapshotted and rolled back to a previous state from a snapshot. Also, among other features that make it increasingly in demand by data centers, it is fault tolerant -- not only at the device level but to an individual file level, able te repair corrupted files -- as long as the required number of redundant storage devices are available and configured appropriately.
Ubuntu offers the ZFS filesystem as an option, which has similar fault tolerant features as Btrfs, but does not have the file level self healing features of Btrfs. While ZFS is an option at installation, the default filesystem on Ubuntu installations is ext4. Users who want the advanced features of Btrfs but originally installed with the default ext4 filesystem can easily convert the ext4 filesystem of the original installation to Btrfs. And if they want the simple system snapshot and rollback management by Snapper, which can incorporate automatic periodic snapshot creation and cleanup, automatic snapshot creation at boot, and automatic pre and post snapshots during APT package management transactions, as well as automatic mounting of the correct subvolume that contains the filesystem hierarchy root because the default Btrfs subvolume is set by Snapper, a little more effort will yield such as system.
The process, similar to the one described in A Fedora Installation with an openSUSE Style BtrfsSubvolume Layout and Snapper Intergration for System Snapshots and Rollbacks, involves using btrfs-convert, part of the set of Btrfs utility progras usually packaged as btrfs-progs, to convert the ext4 filesystem while offline from either a live USB or another installation on the same computer. The external Linux used in performing the process for this article was an Arch Linux installation which was installed on Btrfs with an oenSUSE layout and Snapper integration following the process described in An Arch Linux Installation on a BtrfsFilesystem with Snapper for System Snapshots and Rollbacks. Because this external Linux was Btrfs based, the btrfs-progs package was already available. If the external Linux is not Btrfs based, the package must be installed before performing the filesystem conversion.
After the conversion the openSUSE style subvolume layout was created by mounting the converted filesystem from the external system, moving the directories and files in the filesystem hierarchy root, except those parts of the hierarchy to be excluded from system snapshots (/opt, /root, /var/cache, etc.) to the desired subvolume. Other tasks were also necessary to properly configure the system, including setting the default btrfs subvolume, editing the /etc/fstab file to include the subvolumes containing filesystem hierarchy paths to be excluded from system snapshots, editing GRUB scripts in /etc/grub.d for compatibility with automatic mounting of the subvolume containing the filesystem hierarchy root by /etc/fstab without explicitly specifying the subvolume containing the filesystem hierarchy root as a mount option.
The following graphic helps to illustrates the process and the desired configuration. The right part depicts the final subvolume configuration, including aspects of the configuration such as the subvolume used as the filesystem hierarchy root, how this subvolume is mounted, the changes in the subvolume layout as new snapshots are created, and the effect of rollbacks to the subvolume layout. The left part depicts intermediate states of the system at various stages in the process.
The process is summarized below, before the full process found later in this article:
Before using the btrfs-convert command, the ext4 filesystem must be in a suitable state for conversion. The filesystem must be error free; it is recommended to perform a a check and, if necessary, a repair of the ext4 filesystem with e2fsck command typically available from a package named e2fsprogs. The filesystem must also have adequate free space to perform the conversion; btrfs-convert(8) states that although the necessary free space can not be predicted before the conversion is performed, several GB of free space may be required per 100GB of filesystem size. It may be helpful to delete unnecessary files, check for fragmentation and defragment if necessary with the e4defrag command, also available from the e2fsprogs package.
The last requirement is that the filesystem block size must be one that is supported by btrfs-convert, which is the same as a valid value for the mkfs.btrfs --sectorsize
option. The btrfs-convert man page states that this "is typically the system page size (4KiB on x86_64 machines)", which in my case is the same as the value reported by the tune2fs command.(See Reference 6.)
tune2fs -l /dev/nvme1n1p7
The actual Ubuntu root ext4 filesystem conversion is very simple and only requires executing the btrfs-convert command from the external Linux while the Ubuntu root partition is unmounted. The command has numerous options that allow specifying the characteristics of the converted Btrfs filesystem but can be used without any options to use default options. It only takes one argument which specifies the block device identification of the partition to be converted.
100% 18:18:15 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs-convert /dev/nvme1n1p7The output from this command is shown in the left pane of the terminal in the first two images of the following set of images, the first taken while the command is still running, and the second after the command has completed.
100% 18:40:30 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo mount /dev/nvme1n1p7 /mnt/ubuntu-root
100% 18:40:55 USER: brook HOST: ARCH-16ITH6 ~ ❯$ mount | grep /mnt dev/nvme1n1p7 on /mnt/ubuntu-root type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/)
btrfs filesystem showas shown in the following listing
100% 18:42:20 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs filesystem show /mnt/ubuntu-root/ Label: 'UBUNTU_ROOT' uuid: 72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 Total devices 1 FS bytes used 30.72GiB devid 1 size 72.50GiB used 52.75GiB path /dev/nvme1n1p7and
btrfs subvolume listas shown in the following listing
100% 18:42:31 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume list /mnt/ubuntu-root/ ID 256 gen 3 top level 5 path ext2_savedThe contents of the filesystem can be viewed with the
ls
command with the mountpoint as an argument.
The left pane of the terminal displayed in the third image of the following set shows the output of the above Btrfs commands as well as the directory listing.
100% 18:51:55 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume delete /mnt/ubuntu-root/ext2_saved [sudo] password for brook: Delete subvolume 256 (no-commit): '/mnt/ubuntu-root/ext2_saved'
In Part 2, after converting the ext4 filesystem to Btrfs, we mounted the partition, specifically the top level subvolume -- the only subvolume at this point, and viewed the filesystem statistics. We continue working on the filesystem from our external Linux installation or live ISO.
100% 18:56:42 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume create -p /mnt/ubuntu-root/@ Create subvolume '/mnt/ubuntu-root/@'
100% 18:59:55 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume create -p /mnt/ubuntu-root/@/@opt Create subvolume '/mnt/ubuntu-root/@/@opt' 100% 19:01:31 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume create -p /mnt/ubuntu-root/@/@root Create subvolume '/mnt/ubuntu-root/@/@root' 100% 19:01:40 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume create -p /mnt/ubuntu-root/@/@srv Create subvolume '/mnt/ubuntu-root/@/@srv' 100% 19:01:47 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume create -p /mnt/ubuntu-root/@/@usrlocal Create subvolume '/mnt/ubuntu-root/@/@usrlocal' 100% 19:01:58 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume create -p /mnt/ubuntu-root/@/@varcache Create subvolume '/mnt/ubuntu-root/@/@varcache' 100% 19:02:09 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume create -p /mnt/ubuntu-root/@/@varlog Create subvolume '/mnt/ubuntu-root/@/@varlog' 100% 19:02:16 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume create -p /mnt/ubuntu-root/@/@vartmp Create subvolume '/mnt/ubuntu-root/@/@vartmp'
100% 19:02:24 USER: brook HOST: ARCH-16ITH6 ~ ❯$ sudo btrfs subvolume list /mnt/ubuntu-root/ ID 257 gen 1168 top level 5 path @ ID 258 gen 1167 top level 257 path @/@opt ID 259 gen 1167 top level 257 path @/@root ID 260 gen 1167 top level 257 path @/@srv ID 261 gen 1167 top level 257 path @/@usrlocal ID 262 gen 1168 top level 257 path @/@varcache ID 263 gen 1168 top level 257 path @/@varlog ID 264 gen 1168 top level 257 path @/@vartmp
100% 19:06:54 USER: brook HOST: ARCH-16ITH6 ~ ❯$ ls -la /mnt/ubuntu-root total 32 drwxr-xr-x 1 root root 238 Oct 11 18:59 ./ drwxr-xr-x 1 root root 22 Oct 10 17:16 ../ drwxr-xr-x 1 root root 90 Oct 11 19:02 '@'/ lrwxrwxrwx 1 root root 7 May 27 2022 bin -> usr/bin/ drwxr-xr-x 1 root root 492 Oct 10 19:20 boot/ drwxr-xr-x 1 root root 0 May 27 2022 cdrom/ drwxr-xr-x 1 root root 128 Apr 19 2022 dev/ drwxr-xr-x 1 root root 5084 Oct 10 19:20 etc/ drwxr-xr-x 1 root root 20 Oct 10 18:21 home/ -rw------- 1 root root 64 Oct 10 19:08 i7lp616G-el lrwxrwxrwx 1 root root 7 May 27 2022 lib -> usr/lib/ lrwxrwxrwx 1 root root 9 Oct 3 17:54 lib32 -> usr/lib32/ lrwxrwxrwx 1 root root 9 May 27 2022 lib64 -> usr/lib64/ lrwxrwxrwx 1 root root 10 May 27 2022 libx32 -> usr/libx32/ drwx------ 1 root root 0 May 27 2022 lost+found/ drwxr-xr-x 1 root root 44 Jul 10 2023 media/ drwxr-xr-x 1 root root 42 Nov 24 2023 mnt/ drwxr-xr-x 1 root root 106 Oct 3 18:32 opt/ drwxr-xr-x 1 root root 0 Apr 18 2022 proc/ drwx------ 1 root root 256 Oct 11 18:03 root/ drwxr-xr-x 1 root root 284 May 27 2022 run/ lrwxrwxrwx 1 root root 8 May 27 2022 sbin -> usr/sbin/ drwxr-xr-x 1 root root 18 May 26 2023 snap/ drwxr-xr-x 1 root root 0 Apr 19 2022 srv/ drwxr-xr-x 1 root root 0 Apr 18 2022 sys/ drwxrwxrwt 1 root root 384 Oct 10 19:20 tmp/ drwxr-xr-x 1 root root 116 Dec 2 2023 usr/ drwxr-xr-x 1 root root 130 Oct 3 18:18 var/ -rw------- 1 root root 64 Oct 3 18:13 zNUV1bYp-elNote the @/ directory which allows us to access the newly created /@ subvolume.
100% 19:12:52 USER: brook HOST: ARCH-16ITH6 ~ ❯$ ls -la /mnt/ubuntu-root/@ total 0 drwxr-xr-x 1 root root 90 Oct 11 19:02 ./ drwxr-xr-x 1 root root 238 Oct 11 18:59 ../ drwxr-xr-x 1 root root 0 Oct 11 19:01 '@opt'/ drwxr-xr-x 1 root root 0 Oct 11 19:01 '@root'/ drwxr-xr-x 1 root root 0 Oct 11 19:01 '@srv'/ drwxr-xr-x 1 root root 0 Oct 11 19:01 '@usrlocal'/ drwxr-xr-x 1 root root 0 Oct 11 19:02 '@varcache'/ drwxr-xr-x 1 root root 0 Oct 11 19:02 '@varlog'/ drwxr-xr-x 1 root root 0 Oct 11 19:02 '@vartmp'/As of now, these directories (and their corresponding subvolumes) are empty. In the next part, we will move the contents of the respective filesystem hierarchy paths to the subvolumes.
In this part, we will move the contents of the filesystem hierarchy paths /opt, /root, /srv, /usr/local, /var/cache, /var/log and, /var/tmp to the respective subvolumes created in the last part to contain them, respectively, /@/@opt, /@/@root, /@/srv, /@/@usrlocal, /@/@varcache, /@/@varlog and, /@/@vartmp. The files and directories under these paths will be excluded from system snapshots and remain in these subvolumes in the final system.
We will then move the remaining contents of the top-level subvolume (subvolume path /, subvolume ID 5) to the other subvolume created in the previous step (subvolume path /@, subvolume ID 257). This is an intermediate step before moving these files and directories to their final destination in the initial snapshot subvolume in Part 6, after creating the subvolume for the initial snapshot in Part 5.
100% 19:15:00 USER: brook HOST: ARCH-16ITH6 PCD: 12s ~ ❯$ su Password:
[root@ARCH-16ITH6 brook]# mv -v /mnt/ubuntu-root/opt/* /mnt/ubuntu-root/@/@opt/ created directory '/mnt/ubuntu-root/@/@opt/firefox-beta' copied '/mnt/ubuntu-root/opt/firefox-beta/application.ini' -> '/mnt/ubuntu-root/@/@opt/firefox-beta/application.ini' copied '/mnt/ubuntu-root/opt/firefox-beta/libnss3.so' -> '/mnt/ubuntu-root/@/@opt/firefox-beta/libnss3.so' copied '/mnt/ubuntu-root/opt/firefox-beta/update-settings.ini' -> '/mnt/ubuntu-root/@/@opt/firefox-beta/update-settings.ini' ... truncated ...
[root@ARCH-16ITH6 brook]# mv -v /mnt/ubuntu-root/root/* /mnt/ubuntu-root/@/@root/ ... truncated ...We can confirm all files were moved from /mnt/ubuntu-root/root to /mnt/ubuntu-root/@/@root (from within subvolume path to subvolume /@/@root in terms of the Ubuntu's new Btrfs partition):
[root@ARCH-16ITH6 brook]# ls -la /mnt/ubuntu-root/@/@root total 36 drwxr-xr-x 1 root root 256 Oct 11 19:19 . drwxr-xr-x 1 root root 90 Oct 11 19:02 .. -rw------- 1 root root 1181 Oct 11 18:06 .bash_history -rw-r--r-- 1 root root 3106 Oct 15 2021 .bashrc drwx------ 1 root root 238 Nov 23 2023 .cache drwx------ 1 root root 18 Mar 14 2023 .config drwx------ 1 root root 22 Feb 25 2023 .dbus drwx------ 1 root root 34 May 24 2022 .launchpadlib -rw------- 1 root root 44 Oct 11 18:03 .lesshst drwxr-xr-x 1 root root 20 Nov 27 2023 .local -rw-r--r-- 1 root root 161 Jul 9 2019 .profile drwx------ 1 root root 84 May 27 2022 snap drwx------ 1 root root 0 May 26 2023 .ssh -rw-r--r-- 1 root root 0 Mar 8 2023 .sudo_as_admin_successful drwxr-xr-x 1 root root 20 Dec 2 2023 .vim -rw------- 1 root root 13339 Dec 2 2023 .viminfo -rw-r--r-- 1 root root 165 Nov 24 2023 .wget-hstsNote that in this case all files, including hidden files, have been moved, However, depending on the shell and the external Linux's shell configuration, hidden files may not be moved when using
mv
with the *
glob. (See Reference 7.)
[root@ARCH-16ITH6 brook]# mv -v /mnt/ubuntu-root/usr/local/* /mnt/ubuntu-root/@/@usrlocal/ ... truncated ...
[root@ARCH-16ITH6 brook]# mv -v /mnt/ubuntu-root/var/cache/* /mnt/ubuntu-root/@/@varcache/ ... truncated ...
[root@ARCH-16ITH6 brook]# mv -v /mnt/ubuntu-root/var/log/* /mnt/ubuntu-root/@/@varlog/ ... truncated ...
[root@ARCH-16ITH6 brook]# mv -v /mnt/ubuntu-root/var/tmp/* /mnt/ubuntu-root/@/@vartmp/ ... truncated ...
[root@ARCH-16ITH6 brook]# ls -la /mnt/ubuntu-root/ total 32 drwxr-xr-x 1 root root 238 Oct 11 18:59 . drwxr-xr-x 1 root root 22 Oct 10 17:16 .. drwxr-xr-x 1 root root 90 Oct 11 19:02 @ lrwxrwxrwx 1 root root 7 May 27 2022 bin -> usr/bin drwxr-xr-x 1 root root 492 Oct 10 19:20 boot drwxr-xr-x 1 root root 0 May 27 2022 cdrom drwxr-xr-x 1 root root 128 Apr 19 2022 dev drwxr-xr-x 1 root root 5084 Oct 10 19:20 etc drwxr-xr-x 1 root root 20 Oct 10 18:21 home -rw------- 1 root root 64 Oct 10 19:08 i7lp616G-el lrwxrwxrwx 1 root root 7 May 27 2022 lib -> usr/lib lrwxrwxrwx 1 root root 9 Oct 3 17:54 lib32 -> usr/lib32 lrwxrwxrwx 1 root root 9 May 27 2022 lib64 -> usr/lib64 lrwxrwxrwx 1 root root 10 May 27 2022 libx32 -> usr/libx32 drwx------ 1 root root 0 May 27 2022 lost+found drwxr-xr-x 1 root root 44 Jul 10 2023 media drwxr-xr-x 1 root root 42 Nov 24 2023 mnt drwxr-xr-x 1 root root 0 Oct 11 19:16 opt drwxr-xr-x 1 root root 0 Apr 18 2022 proc drwx------ 1 root root 0 Oct 11 19:19 root drwxr-xr-x 1 root root 284 May 27 2022 run lrwxrwxrwx 1 root root 8 May 27 2022 sbin -> usr/sbin drwxr-xr-x 1 root root 18 May 26 2023 snap drwxr-xr-x 1 root root 0 Apr 19 2022 srv drwxr-xr-x 1 root root 0 Apr 18 2022 sys drwxrwxrwt 1 root root 384 Oct 10 19:20 tmp drwxr-xr-x 1 root root 116 Dec 2 2023 usr drwxr-xr-x 1 root root 130 Oct 3 18:18 var -rw------- 1 root root 64 Oct 3 18:13 zNUV1bYp-el
[root@ARCH-16ITH6 brook]# rm -r /mnt/ubuntu-root/{opt,root,srv,usr/local,var/cache,var/log,var/tmp}
[root@ARCH-16ITH6 brook]# ls -la /mnt/ubuntu-root/@ total 0 drwxr-xr-x 1 root root 90 Oct 11 19:02 . drwxr-xr-x 1 root root 218 Oct 11 19:36 .. drwxr-xr-x 1 root root 106 Oct 11 19:16 @opt drwxr-xr-x 1 root root 256 Oct 11 19:19 @root drwxr-xr-x 1 root root 0 Oct 11 19:01 @srv drwxr-xr-x 1 root root 86 Oct 11 19:20 @usrlocal drwxr-xr-x 1 root root 306 Oct 11 19:23 @varcache drwxr-xr-x 1 root root 2808 Oct 11 19:24 @varlog drwxr-xr-x 1 root root 0 Oct 11 19:02 @vartmpNote that it is empty of actual files and directories. The output shows the subvolumes represented as directories since the top level subvolume, subvolume / is mounted in the external Linux at /mnt/ubuntu-root.
[root@ARCH-16ITH6 brook]# mv /mnt/ubuntu-root/* /mnt/ubuntu-root/@/ mv: cannot move '/mnt/ubuntu-root/@' to a subdirectory of itself, '/mnt/ubuntu-root/@/@'The error displayed does not matter; the remaining contents of /mnt/ubuntu-root have been moved to /mnt/ubuntu-root/@ in terms of the external Linux filesystem hierarchy, and in terms of the Btrfssubvolumes on the Ubuntu partition, / to /@. The following listing confirms this.
[root@ARCH-16ITH6 brook]# ls -la /mnt/ubuntu-root/@ total 32 drwxr-xr-x 1 root root 306 Oct 11 19:41 . drwxr-xr-x 1 root root 2 Oct 11 19:41 .. lrwxrwxrwx 1 root root 7 May 27 2022 bin -> usr/bin drwxr-xr-x 1 root root 492 Oct 10 19:20 boot drwxr-xr-x 1 root root 0 May 27 2022 cdrom drwxr-xr-x 1 root root 128 Apr 19 2022 dev drwxr-xr-x 1 root root 5084 Oct 10 19:20 etc drwxr-xr-x 1 root root 20 Oct 10 18:21 home -rw------- 1 root root 64 Oct 10 19:08 i7lp616G-el lrwxrwxrwx 1 root root 7 May 27 2022 lib -> usr/lib lrwxrwxrwx 1 root root 9 Oct 3 17:54 lib32 -> usr/lib32 lrwxrwxrwx 1 root root 9 May 27 2022 lib64 -> usr/lib64 lrwxrwxrwx 1 root root 10 May 27 2022 libx32 -> usr/libx32 drwx------ 1 root root 0 May 27 2022 lost+found drwxr-xr-x 1 root root 44 Jul 10 2023 media drwxr-xr-x 1 root root 42 Nov 24 2023 mnt drwxr-xr-x 1 root root 106 Oct 11 19:16 @opt drwxr-xr-x 1 root root 0 Apr 18 2022 proc drwxr-xr-x 1 root root 256 Oct 11 19:19 @root drwxr-xr-x 1 root root 284 May 27 2022 run lrwxrwxrwx 1 root root 8 May 27 2022 sbin -> usr/sbin drwxr-xr-x 1 root root 18 May 26 2023 snap drwxr-xr-x 1 root root 0 Oct 11 19:01 @srv drwxr-xr-x 1 root root 0 Apr 18 2022 sys drwxrwxrwt 1 root root 384 Oct 10 19:20 tmp drwxr-xr-x 1 root root 106 Oct 11 19:36 usr drwxr-xr-x 1 root root 86 Oct 11 19:20 @usrlocal drwxr-xr-x 1 root root 108 Oct 11 19:36 var drwxr-xr-x 1 root root 306 Oct 11 19:23 @varcache drwxr-xr-x 1 root root 2808 Oct 11 19:24 @varlog drwxr-xr-x 1 root root 0 Oct 11 19:02 @vartmp -rw------- 1 root root 64 Oct 3 18:13 zNUV1bYp-el
[root@ARCH-16ITH6 brook]# umount --recursive /mnt/ubuntu-root
In this part, we will install Snapper and initialize its configuration, which will automatically create a subvolume for snapshots named .snapshots directly under the subvolume mounted at the filesystem hierarchy root when the initialization command is executed; the subvolume path of this subvolume will be at /@/.snapshots. Then we will create the initial snapshot subvolume at subvolume path /@/.snapshots/1/snapshot, which at the end of the process will contain the Ubuntu installation's filesystem hierarchy root, except the paths that are excluded from system snapshots which will be in their own respective subvolumes, e.g., /opt at /@/@opt, /root at /@/@root, etc.
To do this we will chroot into the installed Ubuntu system, mounting the /@ subvolume -- which currently contains the filesystem hierarchy root of the Ubuntu installation after the actions in Part 4 -- at /mnt/ubuntu-root and the other subvolumes at appropriate mountpoints, e.g., /@/@opt at /mnt/ubuntu-root/opt.
[root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root -o subvol=/@ [root@ARCH-16ITH6 brook]# mount UUID="dbe875a9-94a3-4831-8697-0e869ec4a65f" /mnt/ubuntu-root/home [root@ARCH-16ITH6 brook]# mount UUID="CEFD-1322" /mnt/ubuntu-root/boot/efi [root@ARCH-16ITH6 brook]# mount -t proc /proc /mnt/ubuntu-root/proc/ [root@ARCH-16ITH6 brook]# mount -t sysfs /sys /mnt/ubuntu-root/sys/ [root@ARCH-16ITH6 brook]# mount -o bind /dev /mnt/ubuntu-root/dev/ [root@ARCH-16ITH6 brook]# mount -o bind /dev/pts /mnt/ubuntu-root/dev/pts/ [root@ARCH-16ITH6 brook]# mount -o bind /run /mnt/ubuntu-root/run/ [root@ARCH-16ITH6 brook]# mount -o bind /sys/firmware/efi/efivars /mnt/ubuntu-root/sys/firmware/efi/efivars/ [root@ARCH-16ITH6 brook]# mkdir /mnt/ubuntu-root/{opt,root,srv,usr/local,var/cache,var/log,var/tmp} [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/opt -o subvol=/@/@opt [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/root -o subvol=/@/@root [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/srv -o subvol=/@/@srv [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/var/cache -o subvol=/@/@varcache [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/var/log -o subvol=/@/@varlog [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/var/tmp -o subvol=/@/@vartmp [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/usr/local -o subvol=/@/@usrlocal
[root@ARCH-16ITH6 brook]# cp /etc/resolv.conf /mnt/ubuntu-root/etc/resolv.confIf the Ubuntu installation uses systemd-resolved service, /etc/resolv.conf will be a symbolic link to /run/systemd/resolv/stub-resolv.conf. It will be necessary to unlink the file with
unlink /mnt/ubuntu-root/etc/resolv.conf
[root@ARCH-16ITH6 brook]# chroot /mnt/ubuntu-root /bin/bash
root@ARCH-16ITH6:/# cat /etc/os-release PRETTY_NAME="Ubuntu 24.10" NAME="Ubuntu" VERSION_ID="24.10" VERSION="24.10 (Oracular Oriole)" VERSION_CODENAME=oracular ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=oracular LOGO=ubuntu-logo
root@ARCH-16ITH6:/# apt install snapper The following package was automatically installed and is no longer required: libiw30t64 Use 'apt autoremove' to remove it. Installing: snapper Installing dependencies: libbtrfs0t64 libsnapper7t64 Summary: Upgrading: 0, Installing: 3, Removing: 0, Not Upgrading: 1 Download size: 761 kB Space needed: 2,930 kB / 45.8 GB available Continue? [Y/n] Get:1 http://us.archive.ubuntu.com/ubuntu oracular/main amd64 libbtrfs0t64 amd64 6.6.3-1.2 [13.5 kB] Get:2 http://us.archive.ubuntu.com/ubuntu oracular/universe amd64 libsnapper7t64 amd64 0.10.6-1.1build1 [307 kB] Get:3 http://us.archive.ubuntu.com/ubuntu oracular/universe amd64 snapper amd64 0.10.6-1.1build1 [441 kB] Fetched 761 kB in 1s (917 kB/s) Selecting previously unselected package libbtrfs0t64:amd64. (Reading database ... 426979 files and directories currently installed.) Preparing to unpack .../libbtrfs0t64_6.6.3-1.2_amd64.deb ... Unpacking libbtrfs0t64:amd64 (6.6.3-1.2) ... Selecting previously unselected package libsnapper7t64:amd64. Preparing to unpack .../libsnapper7t64_0.10.6-1.1build1_amd64.deb ... Unpacking libsnapper7t64:amd64 (0.10.6-1.1build1) ... Selecting previously unselected package snapper. Preparing to unpack .../snapper_0.10.6-1.1build1_amd64.deb ... Unpacking snapper (0.10.6-1.1build1) ... Setting up libbtrfs0t64:amd64 (6.6.3-1.2) ... Setting up libsnapper7t64:amd64 (0.10.6-1.1build1) ... Setting up snapper (0.10.6-1.1build1) ... Created symlink '/etc/systemd/system/timers.target.wants/snapper-boot.timer' → '/usr/lib/systemd/system/snapper-boot.timer'. Created symlink '/etc/systemd/system/timers.target.wants/snapper-cleanup.timer' → '/usr/lib/systemd/system/snapper-cleanup.timer'. Created symlink '/etc/systemd/system/timers.target.wants/snapper-timeline.timer' → '/usr/lib/systemd/system/snapper-timeline.timer'. Created symlink '/etc/systemd/system/sysinit.target.wants/snapperd.service' → '/usr/lib/systemd/system/snapperd.service'. Running in chroot, ignoring command 'daemon-reload' Running in chroot, ignoring command 'is-active' snapper-boot.service is a disabled or a static unit, not starting it. Running in chroot, ignoring command 'is-active' Running in chroot, ignoring command 'is-active' snapper-cleanup.service is a disabled or a static unit, not starting it. Running in chroot, ignoring command 'is-active' Running in chroot, ignoring command 'is-active' snapper-timeline.service is a disabled or a static unit, not starting it. Running in chroot, ignoring command 'is-active' Running in chroot, ignoring command 'is-active' Running in chroot, ignoring command 'start' Processing triggers for man-db (2.12.1-3) ... Processing triggers for dbus (1.14.10-4ubuntu5) ... Processing triggers for libc-bin (2.40-1ubuntu3) ...
root@ARCH-16ITH6:/# btrfs subvolume list / ID 257 gen 1192 top level 5 path @ ID 258 gen 1171 top level 257 path @opt ID 259 gen 1190 top level 257 path @root ID 260 gen 1177 top level 257 path @srv ID 261 gen 1192 top level 257 path @usrlocal ID 262 gen 1193 top level 257 path @varcache ID 263 gen 1192 top level 257 path @varlog ID 264 gen 1179 top level 257 path @vartmp
root@ARCH-16ITH6:/# snapper --no-dbus -c root create-config /
root@ARCH-16ITH6:/# btrfs subvolume list / ID 257 gen 1192 top level 5 path @ ID 258 gen 1171 top level 257 path @opt ID 259 gen 1190 top level 257 path @root ID 260 gen 1177 top level 257 path @srv ID 261 gen 1192 top level 257 path @usrlocal ID 262 gen 1193 top level 257 path @varcache ID 263 gen 1192 top level 257 path @varlog ID 264 gen 1179 top level 257 path @vartmp ID 265 gen 1194 top level 257 path .snapshots
root@ARCH-16ITH6:/# btrfs subvolume create -p /.snapshots/1/snapshot Create subvolume '/.snapshots/1/snapshot'
root@ARCH-16ITH6:/# btrfs subvolume list / ID 257 gen 1194 top level 5 path @ ID 258 gen 1171 top level 257 path @opt ID 259 gen 1190 top level 257 path @root ID 260 gen 1177 top level 257 path @srv ID 261 gen 1192 top level 257 path @usrlocal ID 262 gen 1193 top level 257 path @varcache ID 263 gen 1192 top level 257 path @varlog ID 264 gen 1179 top level 257 path @vartmp ID 265 gen 1194 top level 257 path .snapshots ID 266 gen 1195 top level 265 path .snapshots/1/snapshot
root@ARCH-16ITH6:/# exit exit
At this point in the process, the contents of the paths /opt, /root, /usr/local, /var/cache, /var/log, and /var/tmp that were in the top-level subvolume immediately after the conversion have been relocated to their respective subvolumes under subvolume path /@. The remaining contents of the top-level subvolume were moved temporarily to /@. In this part we move the contents of /@ to the initial snapshot subvolume, /@/.snapshots/1/snapshot, realizing the desired subvolume layout illustrated in the right part of the image at the top of the article.
[root@ARCH-16ITH6 brook]# mv /mnt/ubuntu-root/* /mnt/ubuntu-root/.snapshots/1/snapshot/ mv: cannot move '/mnt/ubuntu-root/boot/efi' to '/mnt/ubuntu-root/.snapshots/1/snapshot/boot/efi': Device or resource busy mv: cannot move '/mnt/ubuntu-root/dev' to '/mnt/ubuntu-root/.snapshots/1/snapshot/dev': Device or resource busy mv: cannot move '/mnt/ubuntu-root/proc' to '/mnt/ubuntu-root/.snapshots/1/snapshot/proc': Device or resource busy mv: cannot move '/mnt/ubuntu-root/run' to '/mnt/ubuntu-root/.snapshots/1/snapshot/run': Device or resource busy mv: cannot move '/mnt/ubuntu-root/sys' to '/mnt/ubuntu-root/.snapshots/1/snapshot/sys': Device or resource busy
[root@ARCH-16ITH6 brook]# ls -la /mnt/ubuntu-root /mnt/ubuntu-root/.snapshots/1/snapshot /mnt/ubuntu-root: total 0 drwxr-xr-x 1 root root 54 Oct 11 20:32 . drwxr-xr-x 1 root root 22 Oct 10 17:16 .. drwxr-xr-x 1 root root 492 Oct 10 19:20 boot drwxr-xr-x 24 root root 5500 Oct 11 16:50 dev dr-xr-xr-x 505 root root 0 Oct 11 19:51 proc drwxr-xr-x 34 root root 960 Oct 11 18:50 run drwxr-x--- 1 root root 2 Oct 11 20:08 .snapshots dr-xr-xr-x 13 root root 0 Oct 11 20:14 sys /mnt/ubuntu-root/.snapshots/1/snapshot: total 32 drwxr-xr-x 1 root root 300 Oct 11 20:32 . drwxr-xr-x 1 root root 16 Oct 11 20:08 .. lrwxrwxrwx 1 root root 7 May 27 2022 bin -> usr/bin drwxr-xr-x 1 root root 486 Oct 10 19:20 boot drwxr-xr-x 1 root root 0 May 27 2022 cdrom drwxr-xr-x 1 root root 5098 Oct 11 20:06 etc drwxr-xr-x 1 root root 20 Oct 10 18:21 home -rw------- 1 root root 64 Oct 10 19:08 i7lp616G-el lrwxrwxrwx 1 root root 7 May 27 2022 lib -> usr/lib lrwxrwxrwx 1 root root 9 Oct 3 17:54 lib32 -> usr/lib32 lrwxrwxrwx 1 root root 9 May 27 2022 lib64 -> usr/lib64 lrwxrwxrwx 1 root root 10 May 27 2022 libx32 -> usr/libx32 drwx------ 1 root root 0 May 27 2022 lost+found drwxr-xr-x 1 root root 44 Jul 10 2023 media drwxr-xr-x 1 root root 42 Nov 24 2023 mnt drwxr-xr-x 1 root root 106 Oct 11 19:16 @opt drwxr-xr-x 1 root root 0 Oct 11 19:59 opt drwxr-xr-x 1 root root 256 Oct 11 19:19 @root drwxr-xr-x 1 root root 0 Oct 11 19:59 root lrwxrwxrwx 1 root root 8 May 27 2022 sbin -> usr/sbin drwxr-xr-x 1 root root 18 May 26 2023 snap drwxr-xr-x 1 root root 0 Oct 11 19:01 @srv drwxr-xr-x 1 root root 0 Oct 11 19:59 srv drwxrwxrwt 1 root root 384 Oct 10 19:20 tmp drwxr-xr-x 1 root root 116 Oct 11 19:59 usr drwxr-xr-x 1 root root 86 Oct 11 19:20 @usrlocal drwxr-xr-x 1 root root 130 Oct 11 19:59 var drwxr-xr-x 1 root root 306 Oct 11 19:23 @varcache drwxr-xr-x 1 root root 2808 Oct 11 19:24 @varlog drwxr-xr-x 1 root root 0 Oct 11 19:02 @vartmp -rw------- 1 root root 64 Oct 3 18:13 zNUV1bYp-el [root@ARCH-16ITH6 brook]# ls -la /mnt/ubuntu-root/.* /mnt/ubuntu-root/.snapshots/1/snapshot /mnt/ubuntu-root/.snapshots: total 0 drwxr-x--- 1 root root 2 Oct 11 20:08 . drwxr-xr-x 1 root root 54 Oct 11 20:32 .. drwxr-xr-x 1 root root 16 Oct 11 20:08 1 /mnt/ubuntu-root/.snapshots/1/snapshot: total 32 drwxr-xr-x 1 root root 300 Oct 11 20:32 . drwxr-xr-x 1 root root 16 Oct 11 20:08 .. lrwxrwxrwx 1 root root 7 May 27 2022 bin -> usr/bin drwxr-xr-x 1 root root 486 Oct 10 19:20 boot drwxr-xr-x 1 root root 0 May 27 2022 cdrom drwxr-xr-x 1 root root 5098 Oct 11 20:06 etc drwxr-xr-x 1 root root 20 Oct 10 18:21 home -rw------- 1 root root 64 Oct 10 19:08 i7lp616G-el lrwxrwxrwx 1 root root 7 May 27 2022 lib -> usr/lib lrwxrwxrwx 1 root root 9 Oct 3 17:54 lib32 -> usr/lib32 lrwxrwxrwx 1 root root 9 May 27 2022 lib64 -> usr/lib64 lrwxrwxrwx 1 root root 10 May 27 2022 libx32 -> usr/libx32 drwx------ 1 root root 0 May 27 2022 lost+found drwxr-xr-x 1 root root 44 Jul 10 2023 media drwxr-xr-x 1 root root 42 Nov 24 2023 mnt drwxr-xr-x 1 root root 106 Oct 11 19:16 @opt drwxr-xr-x 1 root root 0 Oct 11 19:59 opt drwxr-xr-x 1 root root 256 Oct 11 19:19 @root drwxr-xr-x 1 root root 0 Oct 11 19:59 root lrwxrwxrwx 1 root root 8 May 27 2022 sbin -> usr/sbin drwxr-xr-x 1 root root 18 May 26 2023 snap drwxr-xr-x 1 root root 0 Oct 11 19:01 @srv drwxr-xr-x 1 root root 0 Oct 11 19:59 srv drwxrwxrwt 1 root root 384 Oct 10 19:20 tmp drwxr-xr-x 1 root root 116 Oct 11 19:59 usr drwxr-xr-x 1 root root 86 Oct 11 19:20 @usrlocal drwxr-xr-x 1 root root 130 Oct 11 19:59 var drwxr-xr-x 1 root root 306 Oct 11 19:23 @varcache drwxr-xr-x 1 root root 2808 Oct 11 19:24 @varlog drwxr-xr-x 1 root root 0 Oct 11 19:02 @vartmp -rw------- 1 root root 64 Oct 3 18:13 zNUV1bYp-el
[root@ARCH-16ITH6 brook]# btrfs subvolume list /mnt/ubuntu-root ID 257 gen 1209 top level 5 path @ ID 258 gen 1171 top level 266 path .snapshots/1/snapshot/@opt ID 259 gen 1196 top level 266 path .snapshots/1/snapshot/@root ID 260 gen 1177 top level 266 path .snapshots/1/snapshot/@srv ID 261 gen 1192 top level 266 path .snapshots/1/snapshot/@usrlocal ID 262 gen 1193 top level 266 path .snapshots/1/snapshot/@varcache ID 263 gen 1192 top level 266 path .snapshots/1/snapshot/@varlog ID 264 gen 1179 top level 266 path .snapshots/1/snapshot/@vartmp ID 265 gen 1210 top level 257 path .snapshots ID 266 gen 1211 top level 265 path .snapshots/1/snapshot
[root@ARCH-16ITH6 brook]# mv /mnt/ubuntu-root/.snapshots/1/snapshot/@opt /mnt/ubuntu-root/ [root@ARCH-16ITH6 brook]# mv /mnt/ubuntu-root/.snapshots/1/snapshot/@root /mnt/ubuntu-root/ [root@ARCH-16ITH6 brook]# mv /mnt/ubuntu-root/.snapshots/1/snapshot/@srv /mnt/ubuntu-root/ [root@ARCH-16ITH6 brook]# mv /mnt/ubuntu-root/.snapshots/1/snapshot/@usrlocal /mnt/ubuntu-root/ [root@ARCH-16ITH6 brook]# mv /mnt/ubuntu-root/.snapshots/1/snapshot/@varcache /mnt/ubuntu-root/ [root@ARCH-16ITH6 brook]# mv /mnt/ubuntu-root/.snapshots/1/snapshot/@vartmp /mnt/ubuntu-root/ [root@ARCH-16ITH6 brook]# mv /mnt/ubuntu-root/.snapshots/1/snapshot/@varlog /mnt/ubuntu-root/
[root@ARCH-16ITH6 brook]# btrfs subvolume list /mnt/ubuntu-root ID 257 gen 1212 top level 5 path @ ID 258 gen 1171 top level 257 path @opt ID 259 gen 1196 top level 257 path @root ID 260 gen 1177 top level 257 path @srv ID 261 gen 1192 top level 257 path @usrlocal ID 262 gen 1193 top level 257 path @varcache ID 263 gen 1192 top level 257 path @varlog ID 264 gen 1179 top level 257 path @vartmp ID 265 gen 1210 top level 257 path .snapshots ID 266 gen 1212 top level 265 path .ssnapshots/1/snapshotWe can also verify that files that should be in the subvolume and not in the initial snapshot subvolume are where they should be. For example the contents of what would normally in /var/cache are now in the @varcache subvolume. Later we will mount this subvolume to /var/cache in /etc/fstab, along with the other subvolumes that should be excluded from the snapshot subvolume.
[root@ARCH-16ITH6 brook]# ls -la /mnt/ubuntu-root/.snapshots/1/snapshot/var/cache /mnt/ubuntu-root/@varcache /mnt/ubuntu-root/.snapshots/1/snapshot/var/cache: total 0 drwxr-xr-x 1 root root 0 Oct 11 19:59 . drwxr-xr-x 1 root root 130 Oct 11 19:59 .. /mnt/ubuntu-root/@varcache: total 0 drwxr-xr-x 1 root root 306 Oct 11 19:23 . drwxr-xr-x 1 root root 144 Oct 11 20:45 .. drwxr-xr-x 1 root root 0 May 17 2022 adduser drwxr-xr-x 1 root root 160 Oct 3 18:18 apparmor drwxr-xr-x 1 root root 8 Apr 19 2022 app-info drwxr-xr-x 1 root root 70 Oct 11 20:06 apt drwxr-xr-x 1 root root 120 Apr 19 2022 cracklib drwxrwx--- 1 root 7 112 Oct 10 19:08 cups drwxr-xr-x 1 132 122 0 May 26 2023 cups-browsed drwxr-xr-x 1 root root 146 Oct 10 19:09 debconf drwxr-xr-x 1 root root 310 Apr 19 2022 dictionaries-common drwxr-xr-x 1 root root 695866 Oct 10 19:20 fontconfig drwxr-xr-x 1 root root 0 Sep 4 2021 fonts drwxr-xr-x 1 root root 98 Oct 3 18:11 fwupd drwxr-xr-x 1 129 usbmux 0 Jun 29 2022 fwupdmgr drwx------ 1 root root 18 Oct 11 20:06 ldconfig drwxr-xr-x 1 root root 8 May 27 2022 lightdm drwxr-xr-x 1 6 mail 304 Oct 11 20:06 man drwxr-xr-x 1 root root 18 Apr 19 2022 PackageKit drwx------ 1 root root 0 Feb 25 2023 private drwxr-xr-x 1 root root 0 Mar 31 2023 samba drwxr-xr-x 1 root root 10 Apr 19 2022 swcatalogThis is the case for the /root
[root@ARCH-16ITH6 brook]# ls -la /mnt/ubuntu-root/.snapshots/1/snapshot/root /mnt/ubuntu-root/@root /mnt/ubuntu-root/@root: total 36 drwxr-xr-x 1 root root 256 Oct 11 19:19 . drwxr-xr-x 1 root root 144 Oct 11 20:45 .. -rw------- 1 root root 1390 Oct 11 20:12 .bash_history -rw-r--r-- 1 root root 3106 Oct 15 2021 .bashrc drwx------ 1 root root 238 Nov 23 2023 .cache drwx------ 1 root root 18 Mar 14 2023 .config drwx------ 1 root root 22 Feb 25 2023 .dbus drwx------ 1 root root 34 May 24 2022 .launchpadlib -rw------- 1 root root 44 Oct 11 18:03 .lesshst drwxr-xr-x 1 root root 20 Nov 27 2023 .local -rw-r--r-- 1 root root 161 Jul 9 2019 .profile drwx------ 1 root root 84 May 27 2022 snap drwx------ 1 root root 0 May 26 2023 .ssh -rw-r--r-- 1 root root 0 Mar 8 2023 .sudo_as_admin_successful drwxr-xr-x 1 root root 20 Dec 2 2023 .vim -rw------- 1 root root 13339 Dec 2 2023 .viminfo -rw-r--r-- 1 root root 165 Nov 24 2023 .wget-hsts /mnt/ubuntu-root/.snapshots/1/snapshot/root: total 0 drwxr-xr-x 1 root root 0 Oct 11 19:59 . drwxr-xr-x 1 root root 210 Oct 11 20:45 ..The listings of the other excluded subvolumes are not shown, but they are like /var/cache and /root, in their respective subvolumes.
[root@ARCH-16ITH6 brook]# vim /mnt/ubuntu-root/.snapshots/1/snapshot/etc/fstabAfter the edit the file should look like the following listing (obtained after rebooting into the converted Ubuntu system with the subvolume layout configuration completed).
╭─ user: brook // host: UBUNTU-16ITH6 in ~ as 🧙 took 5m4s ╰─λ cat /etc/fstab # /etc/fstab: static file system information. # # Use 'blkid' to print the universally unique identifier for a # device; this may be used with UUID= as a more robust way to name devices # that works even if disks are added and removed. See fstab(5). # #The before edit and after edit state of the file from the external Linux at this point in the process is shown in the following set of images.# / was on /dev/nvme0n1p7 during installation #UUID=be2c6775-9f46-4851-84da-74a979206977 / ext4 errors=remount-ro 0 1 # After conversion to Btrfs UUID=72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 / btrfs compress=zstd:3 0 0 # /boot/efi was on /dev/nvme0n1p1 during installation UUID=CEFD-1322 /boot/efi vfat umask=0077 0 1 # /home was on /dev/nvme1n1p4 during installation UUID=dbe875a9-94a3-4831-8697-0e869ec4a65f /home ext4 defaults 0 2 # swap was on /dev/nvme1n1p1 during installation UUID=980ddbbf-9b79-4390-be21-850e62b7ebb2 none swap sw 0 0 # After conversion to Btrfs. New .snapshots subvolume UUID=72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 /.snapshots btrfs subvol=@/.snapshots,compress=zstd:3 0 0 # After conversion to Btrfs. New @opt subvolume UUID=72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 /opt btrfs subvol=@/@opt,compress=zstd:3 0 0 # After conversion to Btrfs. New @root subvolume UUID=72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 /root btrfs subvol=@/@root,compress=zstd:3 0 0 # After conversion to Btrfs. New @srv subvolume UUID=72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 /srv btrfs subvol=@/@srv,compress=zstd:3 0 0 # After conversion to Btrfs. New @usrlocal subvolume UUID=72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 /usr/local btrfs subvol=@/@usrlocal,compress=zstd:3 0 0 # After conversion to Btrfs. New @varcache subvolume UUID=72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 /var/cache btrfs subvol=@/@varcache,compress=zstd:3 0 0 # After conversion to Btrfs. New @varlog subvolume UUID=72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 /var/log btrfs subvol=@/@varlog,compress=zstd:3 0 0 # After conversion to Btrfs. New @vartmp subvolume UUID=72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43 /var/tmp btrfs subvol=@/@vartmp,compress=zstd:3 0 0 ############################################################################################################ UUID=4ad30c04-8dc6-4a6b-9052-315a1d22a48f /home/brook/DataEXT4-16ITH6 ext4 defaults 0 2 UUID=3AC1235B3FBAFA8C /home/brook/DataNTFS-16ITH6 ntfs3 uid=1000,gid=100,dmask=022,fmask=133,discard 0 2 UUID=bb03c8fc-c6f8-452d-a28b-360f405b7c97 /home/brook/VMs ext4 defaults 0 2 ╭─ user: brook // host: UBUNTU-16ITH6 in ~ as 🧙 took 3ms ╰─λ
The next step is to modify two of the GRUB scripts in /etc/grub.d that are executed when making the or updating the GRUB configuration file, /etc/grub/grub.cfg by grub-mkconfig, or in Ubuntu by update-grub, which actually executes grub-mkconfig. These two scripts set the subvolume to be used as the root of the filesystem hierarchy in a way that is not compatible with our desired layout and Snapper.
[root@ARCH-16ITH6 brook]# vim /mnt/ubuntu-root/.snapshots/1/snapshot/etc/grub.d/10_linux
[root@ARCH-16ITH6 brook]# vim /mnt/ubuntu-root/.snapshots/1/snapshot/etc/grub.d/20_linux_xen
In our final converted Ubuntu system, the snapshots subvolume will be mounted at /.snapshots filesystem hierarchy path. Remember that in Part 7 we have already created an entry in the Ubuntu installation's /etc/fstab for this mount. We now make a directory to mount our snapshots subvolume. Because in the final system the fileystem hierarchy root is actually the subvolume /@/.snapshots/1/snapshot, and because at this point in the process the Ubuntu installation's /@ subvolume is mounted in the external Linux at /mnt/ubuntu-root, we can make the directory at /mnt/ubuntu-root/.snapshots/1/snapshot/.snapshot. When we reboot into the Ubuntu system, the snapshots subvolume will be accessible at /.snapshots as intended and as requied by Snapper.
[root@ARCH-16ITH6 brook]# mkdir /mnt/ubuntu-root/.snapshots/1/snapshot/.snapshots
[root@ARCH-16ITH6 brook]# btrfs subvolume get-default /mnt/ubuntu-root ID 5 (FS_TREE)
[root@ARCH-16ITH6 brook]# sudo btrfs subvolume list /mnt/ubuntu-root ID 257 gen 1214 top level 5 path @ ID 258 gen 1216 top level 257 path @opt ID 259 gen 1216 top level 257 path @root ID 260 gen 1177 top level 257 path @srv ID 261 gen 1192 top level 257 path @usrlocal ID 262 gen 1215 top level 257 path @varcache ID 263 gen 1215 top level 257 path @varlog ID 264 gen 1216 top level 257 path @vartmp ID 265 gen 1210 top level 257 path .snapshots ID 266 gen 1223 top level 265 path .snapshots/1/snapshotFrom the listing we can see the subvolume ID of the initial snapshot subvolume at subvolume path /@/.snapshots/1/snapshot is 266.
[root@ARCH-16ITH6 brook]# btrfs subvolume set-default 266 /mnt/ubuntu-root
[root@ARCH-16ITH6 brook]# btrfs subvolume get-default /mnt/ubuntu-root ID 266 gen 1223 top level 265 path @/.snapshots/1/snapshot
[root@ARCH-16ITH6 brook]# umount --recursive /mnt/ubuntu-root
[root@ARCH-16ITH6 brook]# mount | grep /mntThere should not be any output.
[root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root
[root@ARCH-16ITH6 brook]# mount | grep /mnt /dev/nvme1n1p7 on /mnt/ubuntu-root type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=266,subvol=/@/.snapshots/1/snapshot)
mount UUID="dbe875a9-94a3-4831-8697-0e869ec4a65f" /mnt/ubuntu-root/home
[root@ARCH-16ITH6 brook]# mkdir /mnt/ubuntu-root/boot/efi [root@ARCH-16ITH6 brook]# mount UUID="CEFD-1322" /mnt/ubuntu-root/boot/efi
[root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/opt -o subvol=/@/@opt [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/root -o subvol=/@/@root [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/srv -o subvol=/@/@srv [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/var/cache -o subvol=/@/@varcache [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/var/log -o subvol=/@/@varlog [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/var/tmp -o subvol=/@/@vartmp [root@ARCH-16ITH6 brook]# mount UUID="72aaa46b-9109-4c3d-9b63-8c8e3cc7fd43" /mnt/ubuntu-root/usr/local -o subvol=/@/@usrlocal
[root@ARCH-16ITH6 brook]# mkdir -p /mnt/ubuntu-root/{dev,dev/pts,proc,run,sys} [root@ARCH-16ITH6 brook]# mount -t proc /proc /mnt/ubuntu-root/proc/ [root@ARCH-16ITH6 brook]# mount -t sysfs /sys /mnt/ubuntu-root/sys/ [root@ARCH-16ITH6 brook]# mount -o bind /dev /mnt/ubuntu-root/dev/ [root@ARCH-16ITH6 brook]# mount -o bind /dev/pts /mnt/ubuntu-root/dev/pts/ [root@ARCH-16ITH6 brook]# mount -o bind /run /mnt/ubuntu-root/run/ [root@ARCH-16ITH6 brook]# mount -o bind /sys/firmware/efi/efivars /mnt/ubuntu-root/sys/firmware/efi/efivars/
[root@ARCH-16ITH6 brook]# cp /etc/resolv.conf /mnt/ubuntu-root/etc/resolv.conf
[root@ARCH-16ITH6 brook]# mount | grep /mnt/ubuntu-root /dev/nvme1n1p7 on /mnt/ubuntu-root type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=266,subvol=/@/.snapshots/1/snapshot) /dev/nvme0n1p4 on /mnt/ubuntu-root/home type ext4 (rw,relatime) /dev/nvme1n1p1 on /mnt/ubuntu-root/boot/efi type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro) /dev/nvme1n1p7 on /mnt/ubuntu-root/opt type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=258,subvol=/@/@opt) /dev/nvme1n1p7 on /mnt/ubuntu-root/root type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=259,subvol=/@/@root) /dev/nvme1n1p7 on /mnt/ubuntu-root/srv type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=260,subvol=/@/@srv) /dev/nvme1n1p7 on /mnt/ubuntu-root/var/cache type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=262,subvol=/@/@varcache) /dev/nvme1n1p7 on /mnt/ubuntu-root/var/log type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=263,subvol=/@/@varlog) /dev/nvme1n1p7 on /mnt/ubuntu-root/var/tmp type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=264,subvol=/@/@vartmp) /dev/nvme1n1p7 on /mnt/ubuntu-root/usr/local type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=261,subvol=/@/@usrlocal) /proc on /mnt/ubuntu-root/proc type proc (rw,relatime) /sys on /mnt/ubuntu-root/sys type sysfs (rw,relatime) dev on /mnt/ubuntu-root/dev type devtmpfs (rw,nosuid,relatime,size=12158680k,nr_inodes=3039670,mode=755,inode64) devpts on /mnt/ubuntu-root/dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000) run on /mnt/ubuntu-root/run type tmpfs (rw,nosuid,nodev,relatime,mode=755,inode64) efivarfs on /mnt/ubuntu-root/sys/firmware/efi/efivars type efivarfs (rw,nosuid,nodev,noexec,relatime)
[root@ARCH-16ITH6 brook]# chroot /mnt/ubuntu-root /bin/bash
root@ARCH-16ITH6:/# cat /etc/os-release PRETTY_NAME="Ubuntu 24.10" NAME="Ubuntu" VERSION_ID="24.10" VERSION="24.10 (Oracular Oriole)" VERSION_CODENAME=oracular ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=oracular LOGO=ubuntu-logo
root@ARCH-16ITH6:/# su
root@ARCH-16ITH6:/# grub-editenv listUnlike in Fedora (see ), there are no GRUB variables in my Ubuntu installation, continuously upgraded since 22.04.
root@ARCH-16ITH6:/# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Ubuntu
Oct 12 20:21:01 UBUNTU-16ITH6 sudo[7250]: brook : TTY=pts/1 ; PWD=/home/brook ; USER=root ; COMMAND=/usr/sbin/grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Ubuntu
root@ARCH-16ITH6:/# update-grub
Oct 12 20:34:27 UBUNTU-16ITH6 sudo[9003]: brook : TTY=pts/1 ; PWD=/home/brook ; USER=root ; COMMAND=/usr/sbin/update-grub
root@ARCH-16ITH6:/# update-initramfs
Oct 14 22:30:38 UBUNTU-16ITH6 sudo[37896]: brook : TTY=pts/2 ; PWD=/home/brook ; USER=root ; COMMAND=/usr/sbin/update-initramfs -v -c -k all
We can now reboot into the converted Ubuntu system, which at this point will have the subvolume layout illustrated in the diagram at the top of the article, similar to the one used by openSUSE. The initial snapshot subvolume is automatically mounted at the filesystem hierarchy root / without explicitly specifying the subvolume identifiers in the /etc/fstab mount options. Snapper has been configured and is usable, fully able to rollback the system and set the new post-rollback read-write snapshot it creates as the new subvolume for the filesystem hierarchy root. Snapper is even integrated into APT (see, below) such that pre and post snapshots are automatically created during package management transactions.
The following image of some Konsole panes with outputs of various commands show the characteristics of the system.