Fedora, in recent releases has used the next generation Btrfs filesystem by default for new installations. Unfortunately, a default installation of Fedora, or Fedora based distributions which closely follow Fedora, such as Ultramarine Linux, do not create a Btrfs subvolume layout which allows some of its advanced snapshotting and rollback features to be used easily -- without requiring Btrfs commands to be issued from an external system, or to allow snapshot and rollback management utilities, such as Snapper to be easily integrated into the system.
This article describes a process in which Fedora's Anaconda installer is used in a live ISO environment to install Fedora on a basic subvolume layout, and then from within the live ISO environment, modify the subvolume layout and other system configuration items so that it conforms to the openSUSE style of subvolume layout. The process also includes full integration of the Snapper utility, as well as automatic snapshot creation at package management transactions with a Snapper DNF plugin, and inclusion of a GRUB submenu which lists available read-only snapshots in which the system can be booted in order to easily initiate a rollback.
The Btrfs filesystem 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.
Fedora, a serious distribution, highly regarded as a development platform, unlike its corporate sponsor, has embraced the innovative next generation filesystem, using it by default in recent releases. Unfortunately, a default installation of Fedora, or distributions based on Fedora, such as the distribution used as a reference in this article -- Ultramarine Linux, reviewed in Ultramarine Linux 39 Review [KDE Plasma Edition] -- do not configure a subvolume layout which allows the Btrfs filesystem's snapshotting and rollback features to be used easily, or integrate with the Snapper utility, which simplifies snapshot and rollback management by, for example, precluding the necessity of performing rollbacks from an external system. Anaconda, Fedora's installer, simply creates the main top-level subvolume (subvolid=5, subvolume=/) as part of the filesystem formatting, and simply writes the system image to this subvolume.
In order to take advantage of Snapper capability an additional subvolume under the subvolume mounted to the root of the filesystem hierarchy standard is required for containing snapshots in a specific hierarchy (as we will see). In fact, the pioneer of Btrfs, openSUSE creates a subvolume @ under the main subvolume to contain all other subvolumes including one for the series of snapshots and other subvolumes used to contain filesystem hierarchy paths which should be excluded from snapshots.
Even the Blivet-GUI advanced partitioning program included in the Anaconda installer is not flexible enough to allow users to create appropriate subvolumes for a layout that is suitable for use with Snapper. After all partitioning parameters are entered, when activated during installation, it creates the subvolumes in the reverse order from the entered order. It also assigns mountpoints to each subvolume immediately after it is created and not after all subvolumes have been created. Because of these Blivet-GUI idiosyncrasies, it is impossible to create nested subvolumes in the openSUSE way which some users may prefer and is required by Snapper for the snapshots subvolume. It will, however, allow a single subvolume to be created under the main subvolume and mounted as an installation target.
This article describes a process performed from within the live ISO environment immediately after the installer finishes to reproduce the openSUSE subvolume layout, install and configure Snapper, install a DNF plugin to automate snapshot creation before and after package management transactions, and install and configure grub-btrfs to include a submenu of bootable snapshots in the main GRUB menu. The following image shows the final subvolume layout after the process, and for comparison, the subvolume layout of a default Fedora Btrfs installation and a minimal subvolume layout of a Fedora installation in which a single subvolume under the top-level subvolume is created for mounting at /. This minimal subvolume layout installation is used as a starting point for the process described in this article.
The process is summarized below:
The process -- while reproducing openSUSE's subvolume layout and fully and natively integrating Snapper in a way that enables all of its advanced features -- is not the most elegant method for making such a system. The most elegant method would be to perform a Kickstart installation which allows complete control of the installation. Unfortunately, the required learning curve does not have a suitable return on the time investment for a Fedora user who is not committed to the Red Hat/Fedora methodology and system administration utility ecosystem. But the inelegant method is sufficient to transform a basic Fedora Btrfs installation with the desired subvolume layout and integration of all of Snapper's capability.
Anaconda, the installer used by Fedora and related distributions, including RHEL, the use in which was discussed in the review of Red Hat Enterprise Linux 9, is a fairly advanced installer, allowing for advanced configurations, including the ability to set up encrypted RAID partitions. It is organized in a "hub and spoke" paradigm where its major parts are launched from a central screen. Users select each in turn, enter and modify installation parameters, and return to the central screen. Once all parts have been visited and parameters entered to the installers satisfaction, the installation can proceed when the final confirmation button is pressed in the central screen. The following images show the central navigation screen and each of the components. Further below, the Blivet-GUI advanced partitioning component is shown in more detail as part of the initial Btrfs subvolume creation process.
Selecting "Installation Destination" under the heading "SYSTEM" (Images 2 and 6, above) starts the Anaconda partitioning and storage configuration component. A screen that lists all available storage devices, from which installation targets may be chosen is shown. The screen also allows manually specifying network storage and other specialized disks, as well as providing checkboxes for initiating encryption configuration workflow. User's can also specify whether Anaconda should automatically partition available storage or whether the custom partitioning tools should be used, one of which is the Blivet-GUI partitioning tool.
We begin our Btrfs installation configuration on this screen by selecting the disks and checking the Blivet-GUI radio button. In this case, both the SKHynix and Samsung disks were chosen, the first on which to create the root partition, the second for the home partition. Image 1, below shows this screen before selections, and Image 2 shows the screen after the selections.
Selecting "Done" at the top of the "Installation Destination" screen opens the Blivet-GUI interface embedded into Anaconda. This screen lists disks and existing Btrfs subvolumes on the detected disks in the left frame. The top part of the main frame shows the physical layout of disks selected in the left frame, and the bottom part of the right frame shows a list of partitions available if a disk is selected in the left frame, and a list of lower level subvolumes, if a subvolume is chosen in the left frame. Image 3 shows the initial state of this screen on the Lenovo Legion 5i Pro with an existing openSUSE Tumbleweed installation with its default Btrfs subvolume layout, an existing Arch Linux installation with an openSUSE style Btrfs/Snapper configuration, and a Garuda installation on a Btrfs filesystem with a subvolume layout natively compatible with GRUB. The Ultramarine installation will the follow the same layout as the openSUSE and Arch installations, with the exception of creating a subvolume for the GRUB configuration (see Creating openSUSE-style btrfs root partition & subvolumes and An Arch Linux Installation on a Btrfs Filesystem with Snapper for System Snapshots and Rollbacks). This screen is shown in all of the Images 3 - 14 below, with the images illustrating the sequence of interactions in creating our Btrfs subvolumes for the installation. This initial subvolume layout will be significantly modified by the process that follows in the rest of the article.
The root and home partitions -- the root on the SKHynix and the home on the Samsung -- are created by selecting free space on the disks and activating the New" context menu. Image 3 shows the selected free space on the SKHynix and Image 4 shows the dialog which results when activating the "New" menu item, allowing specification of the filesystem format, where "Btrfs Volume" is selected in the dropdown menu. This will cause a new dialog to open specific to the format selected, allowing specification of a partition name and other details as shown in Image 5. The interactions in these two dialogs will result in the creation of a partition with a Btrfs format and the creation by default of the main, top-level subvolume (subvolid=5). The second dialog also allows the specification of a mountpoint, but this should be left blank as we will create a lower level subvolume that will be mounted instead.
Image 6 shows the state of the Blivet-GUI interface after selecting "OK" in the previous dialog. The main subvolume of the new Btrfs partition is now shown in the left pane as ULTRAMARINE_ROOT, the name we previously entered when creating the partition. Selecting the new Btrfs partition in the left pane will display the subvolumes within it on the right pane, in this case there is only one. Activating the context menu on the subvolume shown in the right pane and selecting "New" or clicking the "+" toolbar item will cause the creation of a new subvolume under the selected main subvolume. Image 7 shows the resulting dialog in which the subvolume name and mountpoint can be specified. A subvolume name of "@", a name used by convention for the subvolume to be mounted at the filesystem hierarchy, is specified. The root of the filesystem hierarchy path, /, is specified as the mountpoint for the @ subvolume. The entries in the dialog will cause the installation image to be copied to subvolume @ when we proceed with the installation. It will also cause this subvolume to be specified as the subvolume to be mounted at / in /etc/fstab.
After confirming the creation of the @ subvolume, the state of the Blivet-GUI interface is shown in Image 8, which indicates that the @ is under the main subvolume, but the utility has appended "-0 to the name specified in the previous dialog. This renaming may be to satisfy a requirement for minimum length, or to distinguish other accessible Btrfs subvolumes with similar names. As we will see when we create a separate Btrfs partition later for /home with an @ subvolume under the main subvolume in that partition, a "-1" will be appended to the name. The order of creating these subvolumes affects what is appended to the entered name; if the home Btrfs partition had been created first, it would have received the "-0".
Image 9 shows the beginning of the same process for the home partition. This time we use the context menu instead of the toolbar to create the partition from free space. In Image 10, we specify "Btrfs Volume" as before. In Image 11, which shows the resulting dialog, we specify a name for the partition, but not a mount point. Image 12 shows the main Blivet-GUI interface after the partition specification has been accepted. Here we use the context menu with the new subvolume selected to create a lower level subvolume. In the dialog which opens (Image 13), we specify a name for the new subvoume that will be created under the top-level subvolume as "@" as well as a mount point of /home. Image 14 shows the main Blivet-GUI screen after with the home partition selected in the left pane. The right pane shows that the lower level subvolume has been renamed, as mentioned before, and that it will be mounted to /home for the installation.
Image 15 shows the activation of the "Set mountpoint" context menu item while the ESP (EFI System Partition) is selected, which is itself under the "Edit" menu item. In the resulting dialog, Image 16, the mountpoint "/boot/efi" is specified.
At this point our initial subvolume layout specification, used only for the installation, is complete. We can press "Done" on the main Blivet-GUI interface (Image 17). A summary of actions the partitioner will take is then displayed by the installer in a new dialog, Image 18, providing an opportunity to go back and make changes or accept the configuration. Pressing "Accept Changes will exit Blivet-GUI and return to the main Anaconda screen, where we can initiate the actual installation.
After closing the Anaconda installer window we will remain in the live environment to perform our modifications to the subvolume layout and other aspects of the installed system. We begin by activating a chroot environment into the installed system and install Snapper. We then initialize the Snapper configuration using the snapper command
snapper --no-dbus create-config /mnt
This command will create the configuration file /etc/snapper/configs/root that will be used to define the operating parameters for snapshots. The command also creates the subvolume required by Snapper to contain snapshots named .snapshots under the subvolume currently mounted at / as well as the corresponding directory, /.snapshots to which the subvolume will be mounted.
liveuser on localhost-live ~ ❯ sudo blkid | grep ULTRA /dev/nvme0n1p5: LABEL="ULTRAMARINE_HOME" UUID="217d430a-c396-4d44-bc53-bd6a063a4a78" UUID_SUB="eab6bba2-b002-4314-9c57-396c2327b907" BLOCK_SIZE="4096" TYPE="btrfs" PARTUUID="a5edf838-2e3e-4838-b4d6-d9e81ea49d6e" /dev/nvme1n1p8: LABEL="ULTRAMARINE_ROOT" UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" UUID_SUB="b3616b8d-58d4-4fe6-836a-4266ab1af45b" BLOCK_SIZE="4096" TYPE="btrfs" PARTUUID="bec99faa-f38a-4a07-b5b7-cb101487747c" liveuser on localhost-live ~ ❯ sudo blkid | grep SYSTEM /dev/nvme1n1p1: LABEL_FATBOOT="SYSTEM_DRV" LABEL="SYSTEM_DRV" UUID="CEFD-1322" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="96a25ddb-ffc6-457a-8fd1-66c5825235eb" liveuser on localhost-live ~ ❯ sudo blkid | grep swap /dev/nvme0n1p1: UUID="980ddbbf-9b79-4390-be21-850e62b7ebb2" TYPE="swap" PARTUUID="1356cf0e-d20d-433a-ad5f-3fd564d2b5b7" /dev/zram0: LABEL="zram0" UUID="6d5c4a76-902b-4f35-bee9-db20344fb0e2" TYPE="swap"
liveuser on localhost-live ~ ❯ mount | grep mnt /dev/nvme1n1p8 on /mnt/sysimage type btrfs (rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/@-0) /dev/nvme1n1p1 on /mnt/sysimage/boot/efi type vfat (rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro) devtmpfs on /mnt/sysimage/dev type devtmpfs (rw,nosuid,seclabel,size=4096k,nr_inodes=3035408,mode=755,inode64) devpts on /mnt/sysimage/dev/pts type devpts (rw,relatime,seclabel,gid=5,mode=620,ptmxmode=000) tmpfs on /mnt/sysimage/dev/shm type tmpfs (rw,relatime,seclabel,inode64) /dev/nvme0n1p5 on /mnt/sysimage/home type btrfs (rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/@-1) proc on /mnt/sysimage/proc type proc (rw,relatime) tmpfs on /mnt/sysimage/run type tmpfs (rw,nosuid,nodev,seclabel,size=4876932k,nr_inodes=819200,mode=755,inode64) sysfs on /mnt/sysimage/sys type sysfs (rw,relatime,seclabel) efivarfs on /mnt/sysimage/sys/firmware/efi/efivars type efivarfs (rw,relatime) selinuxfs on /mnt/sysimage/sys/fs/selinux type selinuxfs (rw,relatime) tmpfs on /mnt/sysimage/tmp type tmpfs (rw,relatime,seclabel,inode64) /dev/nvme1n1p8 on /mnt/sysroot type btrfs (rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/@-0) /dev/nvme1n1p1 on /mnt/sysroot/boot/efi type vfat (rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro) devtmpfs on /mnt/sysroot/dev type devtmpfs (rw,nosuid,seclabel,size=4096k,nr_inodes=3035408,mode=755,inode64) devpts on /mnt/sysroot/dev/pts type devpts (rw,relatime,seclabel,gid=5,mode=620,ptmxmode=000) tmpfs on /mnt/sysroot/dev/shm type tmpfs (rw,relatime,seclabel,inode64) /dev/nvme0n1p5 on /mnt/sysroot/home type btrfs (rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/@-1) proc on /mnt/sysroot/proc type proc (rw,relatime) tmpfs on /mnt/sysroot/run type tmpfs (rw,nosuid,nodev,seclabel,size=4876932k,nr_inodes=819200,mode=755,inode64) sysfs on /mnt/sysroot/sys type sysfs (rw,relatime,seclabel) efivarfs on /mnt/sysroot/sys/firmware/efi/efivars type efivarfs (rw,relatime) selinuxfs on /mnt/sysroot/sys/fs/selinux type selinuxfs (rw,relatime) tmpfs on /mnt/sysroot/tmp type tmpfs (rw,relatime,seclabel,inode64)It is necessary to unmount these since we will be modifying the Btrfs subvolumes layout and so we can reuse /mnt. Unmount the mounts at /mnt/sysroot and /mnt/sysimage created by the installer.
liveuser on localhost-live ~ ❯ sudo umount --recursive /mnt/sysroot liveuser on localhost-live ~ ❯ sudo umount --recursive /mnt/sysimage
liveuser on localhost-live ~ ❯ mount | grep mnt
liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt -o subvol=/@-0
liveuser on localhost-live ~ ❯ sudo mount UUID="217d430a-c396-4d44-bc53-bd6a063a4a78" /mnt/home -o subvol=/@-1
liveuser on localhost-live ~ ❯ sudo mount UUID="CEFD-1322" /mnt/boot/efi
liveuser on localhost-live ~ ❯ sudo mount --bind /dev /mnt/dev liveuser on localhost-live ~ ❯ sudo mount -t proc /proc /mnt/proc liveuser on localhost-live ~ ❯ sudo mount -t sysfs /sys /mnt/sys liveuser on localhost-live ~ ❯ sudo mount -t tmpfs tmpfs /mnt/run liveuser on localhost-live ~ ❯ sudo mount -o bind /dev/pts /mnt/dev/pts liveuser on localhost-live ~ ❯ sudo mount -o bind /sys/firmware/efi/efivars /mnt/sys/firmware/efi/efivars
liveuser on localhost-live ~ ❯ sudo cp /etc/resolv.conf /mnt/etc/resolv.conf
liveuser on localhost-live ~ ❯ sudo chroot /mnt /usr/bin/bashThe chroot has been successfully activated if the prompt changes to [root@localhost-live /]#.
[root@localhost-live /]# dnf install snapperThe command with the output:
[root@localhost-live /]# dnf install snapper Fedora 39 - x86_64 6.9 MB/s | 89 MB 00:12 Fedora 39 openh264 (From Cisco) - x86_64 868 B/s | 2.6 kB 00:03 Fedora 39 - x86_64 - Updates 4.0 MB/s | 34 MB 00:08 RPM Fusion for Fedora 39 - Free 294 kB/s | 639 kB 00:02 RPM Fusion for Fedora 39 - Free - Updates 130 kB/s | 234 kB 00:01 RPM Fusion for Fedora 39 - Nonfree 210 kB/s | 259 kB 00:01 RPM Fusion for Fedora 39 - Nonfree - Updates 27 kB/s | 76 kB 00:02 Terra Packages 39 847 kB/s | 2.8 MB 00:03 Ultramarine Linux 39 8.7 kB/s | 30 kB 00:03 Dependencies resolved. ============================================================================================================== Package Architecture Version Repository Size ============================================================================================================== Installing: snapper x86_64 0.10.4-2.fc39 fedora 490 k Installing dependencies: libbtrfs x86_64 6.7.1-1.fc39 updates 27 k snapper-libs x86_64 0.10.4-2.fc39 fedora 365 k Transaction Summary ============================================================================================================== Install 3 Packages Total download size: 883 k Installed size: 2.7 M Is this ok [Y/n]: Downloading Packages: Fedora 39 - x86_64 - Updates 151% [=====================================================] 24 kB/s | 48(1/3): libbtrfs-6.7.1-1.fc39.x86_64.rpm 23 kB/s | 27 kB 00:01 (2/3): snapper-libs-0.10.4-2.fc39.x86_64.rpm 247 kB/s | 365 kB 00:01 (3/3): snapper-0.10.4-2.fc39.x86_64.rpm 260 kB/s | 490 kB 00:01 -------------------------------------------------------------------------------------------------------------- Total 60 kB/s | 883 kB 00:14 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : libbtrfs-6.7.1-1.fc39.x86_64 1/3 Running scriptlet: snapper-libs-0.10.4-2.fc39.x86_64 2/3 Installing : snapper-libs-0.10.4-2.fc39.x86_64 2/3 Installing : snapper-0.10.4-2.fc39.x86_64 3/3 Running scriptlet: snapper-0.10.4-2.fc39.x86_64 3/3 Running scriptlet: snapper-libs-0.10.4-2.fc39.x86_64 3/3 Running scriptlet: snapper-0.10.4-2.fc39.x86_64 3/3 Verifying : snapper-0.10.4-2.fc39.x86_64 1/3 Verifying : snapper-libs-0.10.4-2.fc39.x86_64 2/3 Verifying : libbtrfs-6.7.1-1.fc39.x86_64 3/3 Installed: libbtrfs-6.7.1-1.fc39.x86_64 snapper-0.10.4-2.fc39.x86_64 snapper-libs-0.10.4-2.fc39.x86_64 Complete! [root@localhost-live /]#
[root@localhost-live /]# snapper --no-dbus -c root create-config /The --no-dbus option is necessary because we are in a chroot environment; without it the command would fail. The name of the configuration is root, specified as an argument to the option -c, and the final / specifies the configuration is for snapshots of the subvolume mounted at /.
While our chroot environment is still active, We can see the effect of installing and initializing Snapper in another terminal, as shown in the following listing.
liveuser on localhost-live ~ ❯ sudo btrfs subvolume list /mnt ID 256 gen 29 top level 5 path @-0 ID 257 gen 28 top level 256 path .snapshots liveuser on localhost-live ~ ❯ ls -la /mnt total 20 dr-xr-xr-x. 1 root root 180 Mar 19 06:56 . drwxr-xr-x. 1 root root 260 Mar 19 06:14 .. ... excised ... lrwxrwxrwx. 1 root root 8 Jul 21 2023 sbin -> usr/sbin drwxr-x---. 1 root root 0 Mar 19 06:56 .snapshots drwxr-xr-x. 1 root root 0 Jul 21 2023 srv ... truncated ...
The output of the first command shows that there is now a new subvolume named .snapshots with a subvolume ID of 257 under a parent subvolume named @-0 which has a subvolume ID 256. The output of the second command shows that there is now a directory named .snapshots under the filesystem hierarchy root, which is mounted at /mnt in the live ISO environment.
In the chroot environment, we can continue with the process to move the contents of the subvolume created at installation for the root of the filesystem hierarchy, to a snapshot (a snapshot is a special subvolume) under the .snapshots subvolume. In the SUSE way, which is natively compatible with Snapper, each snapshots is stored under .snapahots within a directory named for the snapshot number. Each time a new snapshots is created by Snapper, a new directory is created for the new snapshot. The initial system is installed to Snapshot 1 by openSUSE's installer at the subvolume path /@/.snapshots/1/snapshot. In our case we will, reproduce this by creating the appropriate subvolume and moving the contents of /@-0 -- created by and set to be mounted at / by Anaconda -- to a subvolume with the same subvolume path as openSUSE's initial snapshot that we will create manually now. a copied to the first snapshot directory. We will move our filesystem hierarchy root from /@-0 to /@-0/.snapshots/1/snapshot.
[root@localhost-live /]# btrfs subvolume create -p /.snapshots/1/snapshot Create subvolume '/.snapshots/1/snapshot'The name of the subvolume is snapshot. The -p option specifies that necessary intermediate directories in the subvolume path be created, in this case creating the directory .snapshots/1. When Snapper is used to create snapshots it will create similar paths incrementing the numbered directory by one each time a snapshot is created. The result of this is a new subvolume at subvolume path /.snapshots/1/snapshot. (Remember that in this path 1 is only a directory, all other elements are actual subvolumes)
[root@localhost-live /]# btrfs subvolume create -p /@opt Create subvolume '//@opt' [root@localhost-live /]# btrfs subvolume create -p /@root Create subvolume '//@root' [root@localhost-live /]# btrfs subvolume create -p /@srv Create subvolume '//@srv' [root@localhost-live /]# btrfs subvolume create -p /@usrlocal Create subvolume '//@usrlocal' [root@localhost-live /]# btrfs subvolume create -p /@varcache Create subvolume '//@varcache' [root@localhost-live /]# btrfs subvolume create -p /@varlog Create subvolume '//@varlog' [root@localhost-live /]# btrfs subvolume create -p /@vartmp Create subvolume '//@vartmp'After the above commands, in a second terminal we can see that the subvolumes have been created:
liveuser on localhost-live ~ ❯ sudo btrfs subvolume list /mnt ID 256 gen 32 top level 5 path @-0 ID 257 gen 31 top level 256 path .snapshots ID 258 gen 31 top level 257 path .snapshots/1/snapshot ID 259 gen 31 top level 256 path @opt ID 260 gen 32 top level 256 path @root ID 261 gen 32 top level 256 path @srv ID 262 gen 32 top level 256 path @usrlocal ID 263 gen 33 top level 256 path @varcache ID 264 gen 33 top level 256 path @varlog ID 265 gen 33 top level 256 path @vartmp
[root@localhost-live /]# exit exit
liveuser on localhost-live ~ ❯umount --recursive /mnt
At this point, our root filesystem hierarchy is in subvolume /@-0. Because of Blivet-GUI's inflexibility, we could not create the subvolumes used to contain the parts of the filesystem hierarchy to be excluded from system snapshots in the desired hierarchical structure as implemented in openSUSE. We will now create these subvolumes after which we will move the contents of subvolume /@-0 mounted at / at installation and currently mounted at /mnt in the live ISO environment, to the initial snapshot subvolume, and the contents of the original subvolume that should be excluded from snapshots to the new subvolumes. This will be a multiple step process where we first move the entire system to the initial snapshot subvolume, then move the excluded paths one by one to the appropriate subvolume.
liveuser on localhost-live ~ ❯ mount | grep mntThere should not be any output.
liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt -o subvol=/@-0Verify the mount
liveuser on localhost-live ~ ❯ mount | grep mnt /dev/nvme1n1p8 on /mnt type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/@-0)
btrfs subvolume create /@rootwhere in this case the / in /@root was in fact the /@-0 subvolume, because it was mounted at /mnt and /mnt became the root of the filesystem hierarchy in the chroot, a corresponding directory was created at /mnt/@root. Directories for the other subvolumes created were also made. We can see these with:
liveuser on localhost-live ~ ❯ ls -la /mnt | grep @ drwxr-xr-x. 1 root root 0 Mar 19 06:59 @opt drwxr-xr-x. 1 root root 0 Mar 19 06:59 @root drwxr-xr-x. 1 root root 0 Mar 19 06:59 @srv drwxr-xr-x. 1 root root 0 Mar 19 07:00 @usrlocal drwxr-xr-x. 1 root root 0 Mar 19 07:00 @varcache drwxr-xr-x. 1 root root 0 Mar 19 07:00 @varlog drwxr-xr-x. 1 root root 0 Mar 19 07:00 @vartmp
liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/@opt -o subvol=/@-0/@opt liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/@root -o subvol=/@-0/@root liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/@srv -o subvol=/@-0/@srv liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/@usrlocal -o subvol=/@-0/@usrlocal liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/@varcache -o subvol=/@-0/@varcache liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/@varlog -o subvol=/@-0/@varlog liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/@vartmp -o subvol=/@-0/@vartmpVerify that the subvolumes were mounted.
liveuser on localhost-live ~ ❯ mount | grep mnt /dev/nvme1n1p8 on /mnt type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/@-0) /dev/nvme1n1p8 on /mnt/@opt type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=259,subvol=/@-0/@opt) /dev/nvme1n1p8 on /mnt/@root type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=260,subvol=/@-0/@root) /dev/nvme1n1p8 on /mnt/@srv type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=261,subvol=/@-0/@srv) /dev/nvme1n1p8 on /mnt/@usrlocal type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=262,subvol=/@-0/@usrlocal) /dev/nvme1n1p8 on /mnt/@varcache type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=263,subvol=/@-0/@varcache) /dev/nvme1n1p8 on /mnt/@varlog type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=264,subvol=/@-0/@varlog) /dev/nvme1n1p8 on /mnt/@vartmp type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=265,subvol=/@-0/@vartmp)
liveuser on localhost-live ~ ❯ su
[root@localhost-live liveuser]# ls -la /mnt /mnt/.snapshots/1/snapshot /mnt: total 20 dr-xr-xr-x. 1 root root 270 Mar 19 07:00 . drwxr-xr-x. 1 root root 260 Mar 19 06:14 .. dr-xr-xr-x. 1 root root 0 Jul 21 2023 afs lrwxrwxrwx. 1 root root 7 Jul 21 2023 bin -> usr/bin dr-xr-xr-x. 1 root root 618 Mar 19 06:38 boot -rw-r--r--. 1 root root 191 Jan 2 00:59 .buildstamp drwxr-xr-x. 1 root root 0 Mar 19 06:35 dev drwxr-xr-x. 1 root root 4488 Mar 19 06:55 etc drwxr-xr-x. 1 root root 0 Mar 19 06:35 home lrwxrwxrwx. 1 root root 7 Jul 21 2023 lib -> usr/lib lrwxrwxrwx. 1 root root 9 Jul 21 2023 lib64 -> usr/lib64 drwxr-xr-x. 1 root root 0 Jul 21 2023 media drwxr-xr-x. 1 root root 0 Jul 21 2023 mnt drwxr-xr-x. 1 root root 0 Mar 19 06:59 @opt drwxr-xr-x. 1 root root 0 Jul 21 2023 opt drwxr-xr-x. 1 root root 0 Mar 19 06:35 proc drwxr-xr-x. 1 root root 0 Mar 19 06:59 @root dr-xr-x---. 1 root root 188 Mar 19 07:01 root drwxr-xr-x. 1 root root 0 Mar 19 06:35 run lrwxrwxrwx. 1 root root 8 Jul 21 2023 sbin -> usr/sbin drwxr-x---. 1 root root 2 Mar 19 06:59 .snapshots drwxr-xr-x. 1 root root 0 Mar 19 06:59 @srv drwxr-xr-x. 1 root root 0 Jul 21 2023 srv drwxr-xr-x. 1 root root 0 Mar 19 06:35 sys drwxrwxrwt. 1 root root 0 Mar 19 06:55 tmp drwxr-xr-x. 1 root root 168 Jan 2 00:52 usr drwxr-xr-x. 1 root root 0 Mar 19 07:00 @usrlocal drwxr-xr-x. 1 root root 180 Jan 2 00:55 var drwxr-xr-x. 1 root root 0 Mar 19 07:00 @varcache drwxr-xr-x. 1 root root 0 Mar 19 07:00 @varlog drwxr-xr-x. 1 root root 0 Mar 19 07:00 @vartmp /mnt/.snapshots/1/snapshot: total 0 drwxr-xr-x. 1 root root 0 Mar 19 06:59 . drwxr-xr-x. 1 root root 16 Mar 19 06:59 ..Note that before the mv command, /mnt/.snapshots/1/snapshot is empty but /mnt is populated with the files and directories that should be in /.
[root@localhost-live liveuser]# mv /mnt/* /mnt/.snapshots/1/snapshot/
[root@localhost-live liveuser]# mv /mnt/* /mnt/.snapshots/1/snapshot/ mv: cannot move '/mnt/@opt' to '/mnt/.snapshots/1/snapshot/@opt': Device or resource busy mv: cannot move '/mnt/@root' to '/mnt/.snapshots/1/snapshot/@root': Device or resource busy mv: cannot move '/mnt/@srv' to '/mnt/.snapshots/1/snapshot/@srv': Device or resource busy mv: cannot move '/mnt/@usrlocal' to '/mnt/.snapshots/1/snapshot/@usrlocal': Device or resource busy mv: cannot move '/mnt/@varcache' to '/mnt/.snapshots/1/snapshot/@varcache': Device or resource busy mv: cannot move '/mnt/@varlog' to '/mnt/.snapshots/1/snapshot/@varlog': Device or resource busy mv: cannot move '/mnt/@vartmp' to '/mnt/.snapshots/1/snapshot/@vartmp': Device or resource busyBut what the output is indicating, -- that the subvolumes it references could not be moved is exactly what we want; we don't want the @opt, @root, @srv, @usrlocal, @varcache, @varlog or @vartmp subvolumes to be moved beneath /@-0/.snapshots/1/snapshot/. The direcories /opt, /root/srv/usr/local/var/cache/var/log or /var/tmp -- in terms of the installed system filessytem hierarchy -- did move along with everything else, except hidden files, in -/ -- from the perspective of the installed system. The lisitng of -/mnt and /mnt/.snapshots/1/snapshot after the above mv command is shown below for comparison from the listing before the move.
[root@localhost-live liveuser]# ls -la /mnt /mnt/.snapshots/1/snapshot /mnt: total 4 dr-xr-xr-x. 1 root root 132 Mar 19 07:11 . drwxr-xr-x. 1 root root 260 Mar 19 06:14 .. -rw-r--r--. 1 root root 191 Jan 2 00:59 .buildstamp drwxr-xr-x. 1 root root 0 Mar 19 06:59 @opt drwxr-xr-x. 1 root root 0 Mar 19 06:59 @root drwxr-x---. 1 root root 2 Mar 19 06:59 .snapshots drwxr-xr-x. 1 root root 0 Mar 19 06:59 @srv drwxr-xr-x. 1 root root 0 Mar 19 07:00 @usrlocal drwxr-xr-x. 1 root root 0 Mar 19 07:00 @varcache drwxr-xr-x. 1 root root 0 Mar 19 07:00 @varlog drwxr-xr-x. 1 root root 0 Mar 19 07:00 @vartmp /mnt/.snapshots/1/snapshot: total 16 drwxr-xr-x. 1 root root 138 Mar 19 07:11 . drwxr-xr-x. 1 root root 16 Mar 19 06:59 .. dr-xr-xr-x. 1 root root 0 Jul 21 2023 afs lrwxrwxrwx. 1 root root 7 Jul 21 2023 bin -> usr/bin dr-xr-xr-x. 1 root root 618 Mar 19 06:38 boot drwxr-xr-x. 1 root root 0 Mar 19 06:35 dev drwxr-xr-x. 1 root root 4488 Mar 19 06:55 etc drwxr-xr-x. 1 root root 0 Mar 19 06:35 home lrwxrwxrwx. 1 root root 7 Jul 21 2023 lib -> usr/lib lrwxrwxrwx. 1 root root 9 Jul 21 2023 lib64 -> usr/lib64 drwxr-xr-x. 1 root root 0 Jul 21 2023 media drwxr-xr-x. 1 root root 0 Jul 21 2023 mnt drwxr-xr-x. 1 root root 0 Jul 21 2023 opt drwxr-xr-x. 1 root root 0 Mar 19 06:35 proc dr-xr-x---. 1 root root 188 Mar 19 07:01 root drwxr-xr-x. 1 root root 0 Mar 19 06:35 run lrwxrwxrwx. 1 root root 8 Jul 21 2023 sbin -> usr/sbin drwxr-xr-x. 1 root root 0 Jul 21 2023 srv drwxr-xr-x. 1 root root 0 Mar 19 06:35 sys drwxrwxrwt. 1 root root 0 Mar 19 06:55 tmp drwxr-xr-x. 1 root root 168 Jan 2 00:52 usr drwxr-xr-x. 1 root root 180 Jan 2 00:55 varWhere before /mnt/.snapshots/1/snapshot was empty, now it is populated with the files that were previously in /mnt, i.e., that were moved into it from /mnt, with the exception of one hidden file and the .snapshots directory, also hidden.
[root@localhost-live liveuser]# mv /mnt/.* /mnt/.snapshots/1/snapshot/ mv: cannot move '/mnt/.snapshots' to a subdirectory of itself, '/mnt/.snapshots/1/snapshot/.snapshots'For the same reasons as the @... subvolumes, .snapshots could not be moved, but the one hidden file -- .buildstamp was moved, as the listing of /mnt and /mnt/.snapshots/1/snapshot, below, shows. Again, we don't want .snapshots to be moved. We want the subvolume to be under /@-0.
[root@localhost-live liveuser]# ls -la /mnt /mnt/.snapshots/1/snapshot /mnt: total 0 dr-xr-xr-x. 1 root root 110 Mar 19 07:12 . drwxr-xr-x. 1 root root 260 Mar 19 06:14 .. drwxr-xr-x. 1 root root 0 Mar 19 06:59 @opt drwxr-xr-x. 1 root root 0 Mar 19 06:59 @root drwxr-x---. 1 root root 2 Mar 19 06:59 .snapshots drwxr-xr-x. 1 root root 0 Mar 19 06:59 @srv drwxr-xr-x. 1 root root 0 Mar 19 07:00 @usrlocal drwxr-xr-x. 1 root root 0 Mar 19 07:00 @varcache drwxr-xr-x. 1 root root 0 Mar 19 07:00 @varlog drwxr-xr-x. 1 root root 0 Mar 19 07:00 @vartmp /mnt/.snapshots/1/snapshot: total 20 drwxr-xr-x. 1 root root 160 Mar 19 07:12 . drwxr-xr-x. 1 root root 16 Mar 19 06:59 .. dr-xr-xr-x. 1 root root 0 Jul 21 2023 afs lrwxrwxrwx. 1 root root 7 Jul 21 2023 bin -> usr/bin dr-xr-xr-x. 1 root root 618 Mar 19 06:38 boot -rw-r--r--. 1 root root 191 Jan 2 00:59 .buildstamp drwxr-xr-x. 1 root root 0 Mar 19 06:35 dev drwxr-xr-x. 1 root root 4488 Mar 19 06:55 etc drwxr-xr-x. 1 root root 0 Mar 19 06:35 home lrwxrwxrwx. 1 root root 7 Jul 21 2023 lib -> usr/lib lrwxrwxrwx. 1 root root 9 Jul 21 2023 lib64 -> usr/lib64 drwxr-xr-x. 1 root root 0 Jul 21 2023 media drwxr-xr-x. 1 root root 0 Jul 21 2023 mnt drwxr-xr-x. 1 root root 0 Jul 21 2023 opt drwxr-xr-x. 1 root root 0 Mar 19 06:35 proc dr-xr-x---. 1 root root 188 Mar 19 07:01 root drwxr-xr-x. 1 root root 0 Mar 19 06:35 run lrwxrwxrwx. 1 root root 8 Jul 21 2023 sbin -> usr/sbin drwxr-xr-x. 1 root root 0 Jul 21 2023 srv drwxr-xr-x. 1 root root 0 Mar 19 06:35 sys drwxrwxrwt. 1 root root 0 Mar 19 06:55 tmp drwxr-xr-x. 1 root root 168 Jan 2 00:52 usr drwxr-xr-x. 1 root root 180 Jan 2 00:55 var
[root@localhost-live liveuser]# ls -la /mnt/.snapshots/1/snapshot/{opt,srv,var/tmp} /mnt/.snapshots/1/snapshot/opt: total 0 drwxr-xr-x. 1 root root 0 Jul 21 2023 . drwxr-xr-x. 1 root root 160 Mar 19 07:12 .. /mnt/.snapshots/1/snapshot/srv: total 0 drwxr-xr-x. 1 root root 0 Jul 21 2023 . drwxr-xr-x. 1 root root 160 Mar 19 07:12 .. /mnt/.snapshots/1/snapshot/var/tmp: total 0 drwxrwxrwt. 1 root root 0 Mar 19 06:55 . drwxr-xr-x. 1 root root 180 Jan 2 00:55 ..But /root, /usr/local, /var/cache, /var/log are not. We will relocate the contents of these directories to their appropriate separate subvolumes.
[root@localhost-live liveuser]# ls -la /mnt/.snapshots/1/snapshot/root /mnt/@root /mnt/@root: total 0 drwxr-xr-x. 1 root root 0 Mar 19 06:59 . dr-xr-xr-x. 1 root root 110 Mar 19 07:12 .. /mnt/.snapshots/1/snapshot/root: total 32 dr-xr-x---. 1 root root 188 Mar 19 07:01 . drwxr-xr-x. 1 root root 160 Mar 19 07:12 .. -rw-------. 1 root root 1334 Mar 19 06:39 anaconda-ks.cfg -rw-------. 1 root root 357 Mar 19 07:01 .bash_history -rw-r--r--. 1 root root 18 Jul 21 2023 .bash_logout -rw-r--r--. 1 root root 141 Jul 21 2023 .bash_profile -rw-r--r--. 1 root root 429 Jul 21 2023 .bashrc drwxr-xr-x. 1 root root 14 Jan 2 00:59 .config -rw-r--r--. 1 root root 100 Jul 21 2023 .cshrc -rw-r--r--. 1 root root 104 Jan 2 00:59 .gtkrc-2.0 drwx------. 1 root root 0 Jan 2 00:51 .ssh -rw-r--r--. 1 root root 129 Jul 21 2023 .tcshrcThe above listing shows that, at this point, /mnt/@root is empty and /mnt/.snapshots/1/snapshot/root is populated with root's files. Move the non-hidden files:
[root@localhost-live liveuser]# mv /mnt/.snapshots/1/snapshot/root/* /mnt/@root/Then move hidden files:
[root@localhost-live liveuser]# mv /mnt/.snapshots/1/snapshot/root/.* /mnt/@root/The listing of /mnt/.snapshots/1/snapshot/root and /mnt/@root shows that unlike the previous listing, before the move, /mnt/.snapshots/1/snapshot/root is now empty, but /mnt/@root is now populated with the items that were previously in /mnt/.snapshots/1/snapshot/root.
[root@localhost-live liveuser]# ls -la /mnt/.snapshots/1/snapshot/root /mnt/@root /mnt/@root: total 32 drwxr-xr-x. 1 root root 188 Mar 19 07:16 . dr-xr-xr-x. 1 root root 110 Mar 19 07:12 .. -rw-------. 1 root root 1334 Mar 19 06:39 anaconda-ks.cfg -rw-------. 1 root root 357 Mar 19 07:01 .bash_history -rw-r--r--. 1 root root 18 Jul 21 2023 .bash_logout -rw-r--r--. 1 root root 141 Jul 21 2023 .bash_profile -rw-r--r--. 1 root root 429 Jul 21 2023 .bashrc drwxr-xr-x. 1 root root 14 Jan 2 00:59 .config -rw-r--r--. 1 root root 100 Jul 21 2023 .cshrc -rw-r--r--. 1 root root 104 Jan 2 00:59 .gtkrc-2.0 drwx------. 1 root root 0 Jan 2 00:51 .ssh -rw-r--r--. 1 root root 129 Jul 21 2023 .tcshrc /mnt/.snapshots/1/snapshot/root: total 0 dr-xr-x---. 1 root root 0 Mar 19 07:16 . drwxr-xr-x. 1 root root 160 Mar 19 07:12 ..
[root@localhost-live liveuser]# ls -la /mnt/.snapshots/1/snapshot/usr/local /mnt/@usrlocal /mnt/.snapshots/1/snapshot/usr/local: total 0 drwxr-xr-x. 1 root root 90 Jan 2 00:51 . drwxr-xr-x. 1 root root 168 Jan 2 00:52 .. drwxr-xr-x. 1 root root 0 Jul 21 2023 bin drwxr-xr-x. 1 root root 0 Jul 21 2023 etc drwxr-xr-x. 1 root root 0 Jul 21 2023 games drwxr-xr-x. 1 root root 0 Jul 21 2023 include drwxr-xr-x. 1 root root 0 Jul 21 2023 lib drwxr-xr-x. 1 root root 6 Jan 2 00:51 lib64 drwxr-xr-x. 1 root root 0 Jul 21 2023 libexec drwxr-xr-x. 1 root root 0 Jul 21 2023 sbin drwxr-xr-x. 1 root root 38 Jan 2 00:51 share drwxr-xr-x. 1 root root 0 Jul 21 2023 src /mnt/@usrlocal: total 0 drwxr-xr-x. 1 root root 0 Mar 19 07:00 . dr-xr-xr-x. 1 root root 110 Mar 19 07:12 ..Move the non-hidden files and directories.
[root@localhost-live liveuser]# mv /mnt/.snapshots/1/snapshot/usr/local/* /mnt/@usrlocal/Then move hidden files.
[root@localhost-live liveuser]# mv /mnt/.snapshots/1/snapshot/usr/local/.* /mnt/@usrlocal/ mv: cannot stat '/mnt/.snapshots/1/snapshot/usr/local/.*': No such file or directoryThe output indicates that, in this case, there were no hidden files or directories in /mnt/mnt/.snapshots/1/snapshot//usr/local. To compare with the previous listing showing the contents of /mnt/mnt/.snapshots/1/snapshot//usr/local and /mnt/@usrlocal we list the current contents after the move:
/mnt/.snapshots/1/snapshot/usr/local: total 0 drwxr-xr-x. 1 root root 0 Mar 19 07:17 . drwxr-xr-x. 1 root root 168 Jan 2 00:52 .. /mnt/@usrlocal: total 0 drwxr-xr-x. 1 root root 90 Mar 19 07:17 . dr-xr-xr-x. 1 root root 110 Mar 19 07:12 .. drwxr-xr-x. 1 root root 0 Jul 21 2023 bin drwxr-xr-x. 1 root root 0 Jul 21 2023 etc drwxr-xr-x. 1 root root 0 Jul 21 2023 games drwxr-xr-x. 1 root root 0 Jul 21 2023 include drwxr-xr-x. 1 root root 0 Jul 21 2023 lib drwxr-xr-x. 1 root root 6 Jan 2 00:51 lib64 drwxr-xr-x. 1 root root 0 Jul 21 2023 libexec drwxr-xr-x. 1 root root 0 Jul 21 2023 sbin drwxr-xr-x. 1 root root 38 Jan 2 00:51 share drwxr-xr-x. 1 root root 0 Jul 21 2023 srcUnlike in the previous listing of the same command, the empty directory and the directory with contents are now reversed, i.e., the contents of /mnt/mnt/.snapshots/1/snapshot//usr/local have been moved to /mnt/@usrlocal.
[root@localhost-live liveuser]# ls -la /mnt/.snapshots/1/snapshot/var/cache /mnt/@varcache /mnt/.snapshots/1/snapshot/var/cache: total 0 drwxr-xr-x. 1 root root 178 Jan 2 00:53 . drwxr-xr-x. 1 root root 180 Jan 2 00:55 .. drwxrwxr-x. 1 abrt abrt 40 Jan 2 00:53 abrt-di drwxr-xr-x. 1 root root 22 Jan 2 00:51 app-info drwxr-xr-x. 1 root root 0 Jul 21 2023 bpf drwxr-xr-x. 1 root root 1664 Mar 19 06:55 dnf drwxr-xr-x. 1 root root 0 Dec 4 00:00 fwupd drwxr-xr-x. 1 root root 6 Jan 2 00:53 ibus drwxr-xr-x. 1 root root 0 Nov 15 00:00 krb5rcache drwx------. 1 root root 18 Mar 19 06:55 ldconfig drwxr-xr-x. 1 root root 14 Jan 2 00:51 libX11 drwxr-xr-x. 1 root root 122 Jan 2 00:55 man drwxr-xr-x. 1 root root 0 Jul 25 2023 PackageKit drwx------. 1 root root 0 Jan 2 00:51 private drwxr-xr-x. 1 root root 0 Jul 21 2023 realmd drwxr-xr-x. 1 root root 10 Jan 2 00:53 swcatalog /mnt/@varcache: total 0 drwxr-xr-x. 1 root root 0 Mar 19 07:00 . dr-xr-xr-x. 1 root root 110 Mar 19 07:12 ..Move the contents.
[root@localhost-live liveuser]# mv /mnt/.snapshots/1/snapshot/var/cache/* /mnt/@varcache/There are no hidden files, so the above is sufficient. We list the contents of the source and destination after the mv command to compare with the previous contents.
[root@localhost-live liveuser]# ls -la /mnt/.snapshots/1/snapshot/var/cache /mnt/@varcache /mnt/.snapshots/1/snapshot/var/cache: total 0 drwxr-xr-x. 1 root root 0 Mar 19 07:19 . drwxr-xr-x. 1 root root 180 Jan 2 00:55 .. /mnt/@varcache: total 0 drwxr-xr-x. 1 root root 178 Mar 19 07:19 . dr-xr-xr-x. 1 root root 110 Mar 19 07:12 .. drwxrwxr-x. 1 abrt abrt 40 Jan 2 00:53 abrt-di drwxr-xr-x. 1 root root 22 Jan 2 00:51 app-info drwxr-xr-x. 1 root root 0 Jul 21 2023 bpf drwxr-xr-x. 1 root root 1664 Mar 19 06:55 dnf drwxr-xr-x. 1 root root 0 Dec 4 00:00 fwupd drwxr-xr-x. 1 root root 6 Jan 2 00:53 ibus drwxr-xr-x. 1 root root 0 Nov 15 00:00 krb5rcache drwx------. 1 root root 18 Mar 19 06:55 ldconfig drwxr-xr-x. 1 root root 14 Jan 2 00:51 libX11 drwxr-xr-x. 1 root root 122 Jan 2 00:55 man drwxr-xr-x. 1 root root 0 Jul 25 2023 PackageKit drwx------. 1 root root 0 Jan 2 00:51 private drwxr-xr-x. 1 root root 0 Jul 21 2023 realmd drwxr-xr-x. 1 root root 10 Jan 2 00:53 swcatalog/mnt/.snapshots/1/snapshot/var/cache is now empty, while /mnt/@varcahce is now populated.
root@localhost-live liveuser]# ls -la /mnt/.snapshots/1/snapshot/var/log /mnt/@varlog /mnt/.snapshots/1/snapshot/var/log: total 1212 drwxr-xr-x. 1 root root 324 Mar 19 06:40 . drwxr-xr-x. 1 root root 180 Jan 2 00:55 .. drwxr-xr-x. 1 root root 234 Mar 19 06:40 anaconda drwx------. 1 root root 0 Nov 4 00:00 audit drwxr-xr-x. 1 root root 0 Oct 6 00:00 blivet-gui -rw-rw----. 1 root utmp 0 Jan 2 00:51 btmp drwxr-x---. 1 chrony chrony 0 Dec 5 00:00 chrony drwxr-xr-x. 1 root lp 0 Dec 15 00:00 cups -rw-r--r--. 1 root root 377758 Mar 19 06:55 dnf.librepo.log -rw-r--r--. 1 root root 667405 Mar 19 06:55 dnf.log -rw-r--r--. 1 root root 182248 Mar 19 06:55 dnf.rpm.log -rw-r--r--. 1 root root 540 Mar 19 06:55 hawkey.log drwxr-sr-x. 1 root systemd-journal 0 Jan 2 00:51 journal -rw-rw-r--. 1 root utmp 0 Jan 2 00:51 lastlog drwxr-x---. 1 mysql mysql 0 Nov 16 00:00 mariadb drwx------. 1 root root 0 Jul 25 2023 ppp drwx------. 1 root root 0 Jan 2 00:51 private drwxr-xr-x. 1 root root 0 Nov 28 00:00 qemu-ga lrwxrwxrwx. 1 root root 39 Jan 2 00:51 README -> ../../usr/share/doc/systemd/README.logs drwx------. 1 root root 6 Jan 2 00:51 samba drwx------. 1 root root 0 Aug 7 2023 speech-dispatcher drwxr-x---. 1 root root 0 Nov 15 00:00 sssd -rw-------. 1 root root 0 Jan 2 00:51 tallylog -rw-rw-r--. 1 root utmp 0 Jan 2 00:51 wtmp /mnt/@varlog: total 0 drwxr-xr-x. 1 root root 0 Mar 19 07:00 . dr-xr-xr-x. 1 root root 110 Mar 19 07:12 ..Relocate the files.
[root@localhost-live liveuser]# mv /mnt/.snapshots/1/snapshot/var/log/* /mnt/@varlog/There are no hidden files. We list the contents of the source and destination of the move to compare with the previous listing of the contents of these direcories.
[root@localhost-live liveuser]# ls -la /mnt/.snapshots/1/snapshot/var/log /mnt/@varlog /mnt/.snapshots/1/snapshot/var/log: total 0 drwxr-xr-x. 1 root root 0 Mar 19 07:20 . drwxr-xr-x. 1 root root 180 Jan 2 00:55 .. /mnt/@varlog: total 1212 drwxr-xr-x. 1 root root 324 Mar 19 07:20 . dr-xr-xr-x. 1 root root 110 Mar 19 07:12 .. drwxr-xr-x. 1 root root 234 Mar 19 06:40 anaconda drwx------. 1 root root 0 Nov 4 00:00 audit drwxr-xr-x. 1 root root 0 Oct 6 00:00 blivet-gui -rw-rw----. 1 root utmp 0 Jan 2 00:51 btmp drwxr-x---. 1 chrony chrony 0 Dec 5 00:00 chrony drwxr-xr-x. 1 root lp 0 Dec 15 00:00 cups -rw-r--r--. 1 root root 377758 Mar 19 06:55 dnf.librepo.log -rw-r--r--. 1 root root 667405 Mar 19 06:55 dnf.log -rw-r--r--. 1 root root 182248 Mar 19 06:55 dnf.rpm.log -rw-r--r--. 1 root root 540 Mar 19 06:55 hawkey.log drwxr-sr-x. 1 root systemd-journal 0 Jan 2 00:51 journal -rw-rw-r--. 1 root utmp 0 Jan 2 00:51 lastlog drwxr-x---. 1 mysql mysql 0 Nov 16 00:00 mariadb drwx------. 1 root root 0 Jul 25 2023 ppp drwx------. 1 root root 0 Jan 2 00:51 private drwxr-xr-x. 1 root root 0 Nov 28 00:00 qemu-ga lrwxrwxrwx. 1 root root 39 Jan 2 00:51 README -> ../../usr/share/doc/systemd/README.logs drwx------. 1 root root 6 Jan 2 00:51 samba drwx------. 1 root root 0 Aug 7 2023 speech-dispatcher drwxr-x---. 1 root root 0 Nov 15 00:00 sssd -rw-------. 1 root root 0 Jan 2 00:51 tallylog -rw-rw-r--. 1 root utmp 0 Jan 2 00:51 wtmpAs expected /mnt/.snapshots/1/snapshot/var/log is not empty and -/mnt/@varlog is populated with the files and directories that were previously in /mnt/.snapshots/1/snapshot/var/log.
At this point the /etc/fstab is as generated by the Anaconda installer, as shown in the following listing.
# [root@localhost-live liveuser]# vim /mnt/.snapshots/1/snapshot/etc/fstab # # /etc/fstab # Created by anaconda on Tue Mar 19 06:37:05 2024 # # Accessible filesystems, by reference, are maintained under '/dev/disk/'. # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info. # # After editing this file, run 'systemctl daemon-reload' to update systemd # units generated from this file. # UUID=1a773ae9-02a6-4e95-bf07-c500427e652b / btrfs subvol=@-0,compress=zstd:1 0 0 UUID=CEFD-1322 /boot/efi vfat umask=0077,shortname=winnt 0 2 UUID=217d430a-c396-4d44-bc53-bd6a063a4a78 /home btrfs subvol=@-1,compress=zstd:1 0 0
Now that we have moved the contents of the subvolume containing the the root of the filesystem hierarchy (/@-0) to the initial snapshot subvolume we created (/@-0/.snapshots/1/snapshot) and from there moved the filesystem hierarchy paths that should be excluded from snapshots to their own subvolumes, we must modify /etc/fstab to include entries that mount these subvolumes to their appropriate mount paths, and to include an entry that mounts the the /@-0/.snapshots subvolume to /.snapshots in order for Snapper to be able to use the subvolume. Also, since we will be setting the initial snapshot as the default subvolume, and using Snapper's capability to set the default subvolume to a new snapshot after rollbacks, we must remove the subvolume identifier from the entry for / from /etc/fstab. This is the native operation with Snapper and the way openSUSE integrates Snapper into its subvolume layout. After we make the modifications to /etc/fstab here, we will reset the default subvolume to the initial snapshot subvolume -- /.snapshots/1/snapshot.
[root@localhost-live liveuser]# vim /mnt/.snapshots/1/snapshot/etc/fstab
# /etc/fstab # Created by anaconda on Tue Mar 12 22:45:18 2024 # Modified after installation on Tue Mar 13 00:13:00 2024 # Accessible filesystems, by reference, are maintained under '/dev/disk/'. # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info. # # After editing this file, run 'systemctl daemon-reload' to update systemd # units generated from this file. # UUID=5f58f3a4-ba83-4f77-8e6d-a0325d86fd18 / btrfs compress=zstd:3 0 0 UUID=CEFD-1322 /boot/efi vfat umask=0077,shortname=winnt 0 2 UUID=86fe8776-bcb9-4891-81a4-0a3b26fe6b20 /home btrfs subvol=@-1,compress=zstd:3 0 0 UUID=5f58f3a4-ba83-4f77-8e6d-a0325d86fd18 /.snapshots btrfs subvol=@-0/.snapshots,compress=zstd:3 0 0 UUID=5f58f3a4-ba83-4f77-8e6d-a0325d86fd18 /opt btrfs subvol=@-0/@opt,compress=zstd:3 0 0 UUID=5f58f3a4-ba83-4f77-8e6d-a0325d86fd18 /root btrfs subvol=@-0/@root,compress=zstd:3 0 0 UUID=5f58f3a4-ba83-4f77-8e6d-a0325d86fd18 /srv btrfs subvol=@-0/@srv,compress=zstd:3 0 0 UUID=5f58f3a4-ba83-4f77-8e6d-a0325d86fd18 /usr/local btrfs subvol=@-0/@usrlocal,compress=zstd:3 0 0 UUID=5f58f3a4-ba83-4f77-8e6d-a0325d86fd18 /var/cache btrfs subvol=@-0/@varcache,compress=zstd:3 0 0 UUID=5f58f3a4-ba83-4f77-8e6d-a0325d86fd18 /var/log btrfs subvol=@-0/@varlog,compress=zstd:3 0 0 UUID=5f58f3a4-ba83-4f77-8e6d-a0325d86fd18 /var/tmp btrfs subvol=@-0/@vartmp,compress=zstd:3 0 0 UUID=980ddbbf-9b79-4390-be21-850e62b7ebb2 none swap defaults 0 0
[root@localhost-live liveuser]# mkdir /mnt/.snapshots/1/snapshot/.snapshots.
By default, a Fedora installation will have the SELinux operating mode set to "enforcing". Unless we make modifications to SELinux configuration, the modifications we have made in the chroot environment will have caused inconsistencies with SELinux's expectations of the filesystem and cause the system boot to hang during the Plymouth animation with an error
[!!!!!!]Failed to allocate manager object
revealed when opening the boot log by pressing [ESC]. We can avoid this issue by settting SELinux's operating mode mode to "permissive". After the system is completely configured, readers who would like to take full advantage of SELinux can change the mode back to "enforcing".
[root@localhost-live liveuser]# vim /mnt/.snapshots/1/snapshot/etc/selinux/config
SELINUX=enforcingto
SELINUX=permissive
# This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. # See also: # https://docs.fedoraproject.org/en-US/quick-docs/getting-started-with-selinux/#getting-started-with-selinux-selinux-states-and-modes # # NOTE: In earlier Fedora kernel builds, SELINUX=disabled would also # fully disable SELinux during boot. If you need a system with SELinux # fully disabled instead of SELinux running with no policy loaded, you # need to pass selinux=0 to the kernel command line. You can use grubby # to persistently set the bootloader to boot with selinux=0: # # grubby --update-kernel ALL --args selinux=0 # # To revert back to SELinux enabled: # # grubby --update-kernel ALL --remove-args selinux # SELINUX=permissive # SELINUXTYPE= can take one of these three values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected. # mls - Multi Level Security protection. SELINUXTYPE=targeted
Note: Setting SELinux to "permissive" should be enough to avoid the error described above. On the off chance that it is not, SELinux can be completely disabled by rebootign and setting the command line parameter selinux=0.
When a Btrfs filesystem is created by mkfs, a single subvolume known as the top-level subvolume or main subvolume -- which always has the identifiers subvolid=5, and subvolume path / -- is created. When this subvolume is created it is automatically set as the default subvolume. To use Snapper's capability of automatically setting the current system snapshot as the snapshot to boot from, we must set the initial snapshot subvolume, /@-0/.snapshots/1/snapshot, as the default subvolume. This is the ubvolume we created above, to which we moved the contents of the original subvolume (the one created by the installer to contain the root of the filesystem hierarchy). When a rollback is performed, Snapper will create two new snapshots, one a read-only copy of the current system, and another a read-write copy of the snapshot to which to rollback. The second snapshot will be set as the default subvolume such that subsequent boots will use this as the system root, and the one mounted by /etc/fstab.
[root@localhost-live liveuser]# exit exit
liveuser on localhost-live ~ took 41m16s ❯ sudo btrfs subvolume list /mnt ID 256 gen 36 top level 5 path @-0 ID 257 gen 30 top level 256 path .snapshots ID 258 gen 68 top level 257 path .snapshots/1/snapshot ID 259 gen 30 top level 256 path @opt ID 260 gen 39 top level 256 path @root ID 261 gen 30 top level 256 path @srv ID 262 gen 41 top level 256 path @usrlocal ID 263 gen 43 top level 256 path @varcache ID 264 gen 45 top level 256 path @varlog ID 265 gen 31 top level 256 path @vartmpFrom the output, we see that the subvolume /@-0/.snapshots/1/snapshot has a subvolume ID of 258.
liveuser on localhost-live ~ ❯ sudo btrfs subvolume get-default /mnt ID 5 (FS_TREE)
liveuser on localhost-live ~ ❯ sudo btrfs subvolume set-default 258 /mntThe numeric subvolume identifier, i.e. the subvolume ID was used in the above command. According to the btrfs subvolume man page, the subvolume may also be specified by subvolume path, however, an error results that indicates a numeric value should be used, thus the subvolume ID and not subvolume path should be used to identify the subvolume.
liveuser on localhost-live ~ ❯ sudo btrfs subvolume get-default /mnt ID 258 gen 68 top level 257 path @-0/.snapshots/1/snapshotThe default subvolume has changed from subvolume ID 5 (subvolume path /) to subvolume ID 258, subvolume path /@-0/.snapshots/1/snapshot.
liveuser on localhost-live ~ ❯ sudo umount --recursive /mnt
Now we chroot into the installed system again as we did above, this time to modify the GRUB configuration of the installed system. This time, since we have set the initial snapshot subvolume as default, when we moint the Btrfs filesystem on the partition used for the root of the system without any subvolume identifier, the initial snapshot subvolume (subolid=258, subvol=/@-0/.snapshots/1/snapshot will be mounted instead of /@-0.
liveuser on localhost-live ~ ❯ mount | grep mnt
liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mntWe can verify that subolid=258, subvol=/@-0/.snapshots/1/snapshot is the one mounted:
liveuser on localhost-live ~ ❯ mount | grep mnt /dev/nvme1n1p8 on /mnt type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=258,subvol=/@-0/.snapshots/1/snapshot)
liveuser on localhost-live ~ ❯ sudo mount UUID="217d430a-c396-4d44-bc53-bd6a063a4a78" /mnt/home -o subvol=/@-1
liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/opt -o subvol=/@-0/@opt liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/root -o subvol=/@-0/@root liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/srv -o subvol=/@-0/@srv liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/usr/local -o subvol=/@-0/@usrlocal liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/var/cache -o subvol=/@-0/@varcache liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/var/log -o subvol=/@-0/@varlog liveuser on localhost-live ~ ❯ sudo mount UUID="1a773ae9-02a6-4e95-bf07-c500427e652b" /mnt/var/tmp -o subvol=/@-0/@vartmp
liveuser on localhost-live ~ ❯ sudo mount UUID="CEFD-1322" /mnt/boot/efi
liveuser on localhost-live ~ ❯ sudo mount --bind /dev /mnt/dev liveuser on localhost-live ~ ❯ sudo mount -t proc /proc /mnt/proc liveuser on localhost-live ~ ❯ sudo mount -t sysfs /sys /mnt/sys liveuser on localhost-live ~ ❯ sudo mount -t tmpfs tmpfs /mnt/run liveuser on localhost-live ~ ❯ sudo mount -o bind /dev/pts /mnt/dev/pts liveuser on localhost-live ~ ❯ sudo mount -o bind /sys/firmware/efi/efivars /mnt/sys/firmware/efi/efivars
liveuser on localhost-live ~ ❯ sudo cp /etc/resolv.conf /mnt/etc/resolv.conf
liveuser on localhost-live ~ ❯ sudo chroot /mnt /usr/bin/bash [root@localhost-live /]#
[root@localhost-live /]# vim /etc/default/grubModify the file so that the line
GRUB_ENABLE_BLSCFG=truebecomes
GRUB_ENABLE_BLSCFG="false"
SUSE_BTRFS_SNAPSHOT_BOOTING="true"If this parameter is included in /etc/default/grub, it will allow GRUB to use relative paths when searching for kernels to boot by setting the relevant parameter found in one of the grub2-mkconfig sourced scripts, /etc/grub.d/00_header, to be set, as shown in the following listing.
if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] && [ "x${GRUB_FS}" = "xbtrfs" ] ; then cat <The result will be that the paths to the kernel in the GRUB kernel command-line will not include the subvolume path, i.e., they will look like
/boot/vmlinuz-6.7.6-200.fc39.x86_64instead of
/@-0/.snapshots/1/snapshot/boot/vmlinuz-6.7.6-200.fc39.x86_64In my opinion, the latter is preferable. When absolute subvolume paths are used, it allows editing the GRUB configuration to specify a specific snapshot and a kernel path for the boot. This is helpful if there is an unexpected problem with the GRUB configuration.
[root@localhost-live /]# rm -r /boot/loader rm: descend into directory '/boot/loader'? y rm: descend into directory '/boot/loader/entries'? y rm: remove regular file '/boot/loader/entries/a6c93ae8fb4347699af7cd22cd207c55-6.6.8-200.fc39.x86_64.conf'? y rm: remove regular file '/boot/loader/entries/a6c93ae8fb4347699af7cd22cd207c55-0-rescue.conf'? y rm: remove directory '/boot/loader/entries'? y rm: remove directory '/boot/loader'? y
[root@localhost-live /]# rm /boot/grub2/grubenv rm: remove regular file '/boot/grub2/grubenv'? y
[root@localhost-live /]# grub2-mkconfig -o /boot/grub2/grub.cfg Generating grub configuration file ... Found linux image: /boot/vmlinuz-6.6.8-200.fc39.x86_64 Found initrd image: /boot/initramfs-6.6.8-200.fc39.x86_64.img fgrep: warning: fgrep is obsolescent; using grep -F Found linux image: /boot/vmlinuz-0-rescue-a6c93ae8fb4347699af7cd22cd207c55 Found initrd image: /boot/initramfs-0-rescue-a6c93ae8fb4347699af7cd22cd207c55.img fgrep: warning: fgrep is obsolescent; using grep -F Found openSUSE Tumbleweed on /dev/nvme1n1p5 Found Arch Linux on /dev/nvme1n1p6 Found Ubuntu 23.10 (23.10) on /dev/nvme1n1p7 Found Garuda Linux on /dev/nvme1n1p9 Adding boot menu entry for UEFI Firmware Settings ... done
We can now reboot the system. When we reboot (see Booting Issues, below, for possible booting issues and resolutions), the system root will be the subvolume we set as the default subvolume, at subvolume path /@-0/.snapshots/1/snapshot, with filesystem hierarchy paths excluded from root snapshots mounted from their respective separate subvolumes, as we specified in /etc/fstab. After the reboot we will continue the remaining steps of the system configuration by installing the the DNF Snapper plugin and grub-btrfs.
At this point in the process the subvolume layout of our Fedora system is as we want it, in the same layout as openSUSE. The system boots from a snapshot subvolume contained within the /@-0/.snapshots subvolume, specifically the /@-0/.snapshots/1/snapshot subvolume, as this has been set as the default subvolume for the Btrfs filesystem on the partition. (If there are problems with the boot, see the section Booting Issues end of the article, for solutions.)
The image below shows a Konsole window with some command outputs which illustrate some of the Btrfs/Snapper related characteristics of the system. The desktop environment was customized and some other external partitions were added to /etc/fstab before the image was captured, but shows essentially the system as it should be at this point. Unfortunately the mountpoints for the exclusion subvolumes are incorrect, although the listing in section Part 5: Modify /etc/fstab is correct. A corrected listing of /etc/fstab is shown following the next image. The output of the first command:
sudo btrfs subvolume list /
shows that the subvolume hierarchy is as intended with a subvolume named @-0 under the main top-level subvolume. /@-0 contains under it the .snapshots subvolume and the individual subvolumes which contain the parts of the filesystem hierarchy that are excluded from system snapshots, e.g, @root. Also, the current root of the filesystem hierarchy is under the .snapshots suvolume in /@-0/.snapshots/1/snapshot.
The output of the second command:
cat /etc/fstab
shows that the entries in filesystem table is as intended. The entry for / does not include a subvolume identifier as this is unnecessary since the default subvolume is set to the initial snapshot subvolume and is mounted automatically at / and changed automatically by Snapper when the root of the filesystem hierarchy is changed to a different snapshot subvolume as a result of a Snapper rollback. The .snapshots subvolume, /@-0/.snapshots is mounted at /.snapshots, and each of the subvolumes used to contain parts of the filesystem hierarchy to be excluded from snapshots are mounted at their respective paths, e.g., /@-0/@root at . The corrected /etc/fstab is shown below.
The output of the third command:
sudo snapper list
The completion of the command without any error means that there are no problems with the Snapper configuration.
At this point in the process the subvolume layout of our Fedora system is as we want it, in the same layout as openSUSE. The system boots from a snapshot subvolume contained within the /@-0/.snapshots subvolume, specifically the /@-0/.snapshots/1/snapshot subvolume, as this has been set as the default Btrfs subvolume for the Btrfs filesystem on the partition.
In this step we will install the DNF plugin package python3-dnf-plugin-snapper, which will cause snapshots to be created automatically by Snapper immediately before and immediately after package managment transactions (this serves the same role as the snap-pac package in Arch as described in An Arch Linux Installation on a Btrfs Filesystem with Snapper for System Snapshots and Rollbacks)
To compare the effect of this package on our system, the currently existing Snapper snapshots are shown below; at this point, before installing the plugin, there is only one, an apparently dummy snapshot created at Snapper initialization (there is no directory /.snapshots/0).
79% 14:47:12 USER: brook HOST: Ultramarine-16ITH6 ~ ❯$ sudo snapper list # | Type | Pre # | Date | User | Cleanup | Description | Userdata ---+--------+-------+------+------+---------+-------------+--------- 0 | single | | | root | | current |
79% 14:47:43 USER: brook HOST: Ultramarine-16ITH6 ~ ❯$ sudo dnf install python3-dnf-plugin-snapper [sudo] password for brook: Fedora 39 - x86_64 - Updates 17 kB/s | 22 kB 00:01 Fedora 39 - x86_64 - Updates 323 kB/s | 2.7 MB 00:08 google-chrome 1.3 kB/s | 1.3 kB 00:01 RPM Fusion for Fedora 39 - Free - Updates 4.1 kB/s | 3.6 kB 00:00 RPM Fusion for Fedora 39 - Free - Updates 116 kB/s | 241 kB 00:02 RPM Fusion for Fedora 39 - Nonfree - Updates 11 kB/s | 6.5 kB 00:00 RPM Fusion for Fedora 39 - Nonfree - Updates 33 kB/s | 86 kB 00:02 Terra Packages 39 142 B/s | 281 B 00:01 Terra Packages 39 59 kB/s | 202 kB 00:03 Ultramarine Linux 39 139 B/s | 281 B 00:02 Dependencies resolved. ========================================================================================================= Package Architecture Version Repository Size ========================================================================================================= Installing: python3-dnf-plugin-snapper noarch 4.1.2-1.fc39 updates 14 k Installing dependencies: python3-dnf-plugins-extras-common noarch 4.1.2-1.fc39 updates 47 k Transaction Summary ========================================================================================================= Install 2 Packages Total download size: 60 k Installed size: 105 k Is this ok [Y/n]: Downloading Packages: (1/2): python3-dnf-plugin-snapper-4.1.2-1.fc39.noarch.rpm 16 kB/s | 14 kB 00:00 (2/2): python3-dnf-plugins-extras-common-4.1.2-1.fc39.noarch.rpm 47 kB/s | 47 kB 00:00 --------------------------------------------------------------------------------------------------------- Total 28 kB/s | 60 kB 00:02 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : python3-dnf-plugins-extras-common-4.1.2-1.fc39.noarch 1/2 Installing : python3-dnf-plugin-snapper-4.1.2-1.fc39.noarch 2/2 Running scriptlet: python3-dnf-plugin-snapper-4.1.2-1.fc39.noarch 2/2 Verifying : python3-dnf-plugin-snapper-4.1.2-1.fc39.noarch 1/2 Verifying : python3-dnf-plugins-extras-common-4.1.2-1.fc39.noarch 2/2 Installed: python3-dnf-plugin-snapper-4.1.2-1.fc39.noarch python3-dnf-plugins-extras-common-4.1.2-1.fc39.noarch Complete!
Now that the plugin is installed, if we perform some package management tasks, the Snapper snapshot list will have more entries, as shown in the following listing.
97% 15:11:46 USER: brook HOST: Ultramarine-16ITH6 PCD: 4m56s ~ ❯$ sudo snapper list [sudo] password for brook: # | Type | Pre # | Date | User | Cleanup | Description | Userdata ---+--------+-------+---------------------------------+------+---------+-------------------------------+--------- 0 | single | | | root | | current | 2 | pre | | Mon 25 Mar 2024 03:01:02 PM EDT | root | number | /usr/bin/dnf install kontact | 3 | post | 2 | Mon 25 Mar 2024 03:01:20 PM EDT | root | number | /usr/bin/dnf install kontact | 4 | pre | | Mon 25 Mar 2024 03:05:51 PM EDT | root | number | /usr/bin/dnf install kdenlive | 5 | post | 4 | Mon 25 Mar 2024 03:05:59 PM EDT | root | number | /usr/bin/dnf install kdenlive | 6 | pre | | Mon 25 Mar 2024 03:11:25 PM EDT | root | number | /usr/bin/dnf upgrade | 7 | post | 6 | Mon 25 Mar 2024 03:11:46 PM EDT | root | number | /usr/bin/dnf upgrade |
The initial snapshot #1, which is the subvolume that contains are system root (at subvolume path /@-0/.snapshots/1/snapshot) is not shown in the listing. But three pairs of new snapshots are listed, each pair made by the DNF Snapper plugin and Snapper as a result of a package management transaction, the first of each pair being a snapshot made before any changes by the transaction, and the second being a snapshot made after changes to the system by the transaction.
We could at this point perform rollbacks to the system from any of these snapshots replacing the subvolume mounted at / -- currently /@-0/.snapshots/1/snapshot -- with one of the one selected for rollback, e.g., /@-0/.snapshots/6/snapshot. As mentioned before, although the subvolume is not explicitly specified in /etc/fstab the selected snapshot will be the one booted because Snapper will set it as the new default Btrfs subvolume after a rollback. In the current state of the system, We would either have to initiate the rollback from within a potentially broken system or boot by editing the GRUB commandline to boot from the desired snapshot. But after the next step where we install gruib-btrfs, this will not be necessary as the available snapshots will be selectable from the GRUB menu.
At this stage of the process, snapshots are automatically created during DNF package transactions, and we could also create snapshots manually. But the two methods of initiating a rollback available now might be complicated. We have two options to initiate the rollback.
But after we install grub-btrfs in this step of the process,the process will be much simpler. A new submenu will be added to GRUB titled "Fedora Linux Snapshots" -- or in the case of this Fedora based distribution used as the reference for the article, "Ultramarine Linux Snapshots" -- from which we will be able to select a read-only snapshot to boot into. The selected snapshot from this menu will be the one to which the system will be rolled back when issuing the snapper rollback command from withing th read-only snapshot.
A package is not available for grub-btrfs in the normal Fedora repositories. A Fedora COPR repository -- kylegospo/grub-btrfs is available for grub-btrfs, but nfortunately, it hasn't been maintained for some time, so we will have to install it manually from a clone of the project's GitHub repository.
84% 14:52:45 USER: brook HOST: Ultramarine-16ITH6 PCD: 1m14s ~ ❯$ git clone https://github.com/Antynea/grub-btrfs.git Cloning into 'grub-btrfs'... remote: Enumerating objects: 1268, done. remote: Counting objects: 100% (341/341), done. remote: Compressing objects: 100% (145/145), done. remote: Total 1268 (delta 240), reused 261 (delta 192), pack-reused 927 Receiving objects: 100% (1268/1268), 407.93 KiB | 720.00 KiB/s, done. Resolving deltas: 100% (757/757), done.
84% 14:53:27 USER: brook HOST: Ultramarine-16ITH6 PCD: 2s ~ ❯$ cd grub-btrfsThe contents of the directory are shown in the following listing.
86% 14:55:53 USER: brook HOST: Ultramarine-16ITH6 on master [!] PCD: 57s grub-btrfs ❯$ ls -la total 128 drwxr-xr-x. 1 brook brook 312 Mar 25 14:55 . drwx------. 1 brook brook 494 Mar 25 14:56 .. -rwxr-xr-x. 1 brook brook 28843 Mar 25 14:53 41_snapshots-btrfs -rw-r--r--. 1 brook brook 148 Mar 25 14:53 .codespellrc -rw-r--r--. 1 brook brook 6738 Mar 25 14:53 config drwxr-xr-x. 1 brook brook 138 Mar 25 14:53 .git drwxr-xr-x. 1 brook brook 18 Mar 25 14:53 .github -rwxr-xr-x. 1 brook brook 11809 Mar 25 14:53 grub-btrfsd -rw-r--r--. 1 brook brook 823 Mar 25 14:53 grub-btrfsd.confd -rwxr-xr-x. 1 brook brook 305 Mar 25 14:53 grub-btrfsd.initd -rw-r--r--. 1 brook brook 910 Mar 25 14:53 grub-btrfsd.service drwxr-xr-x. 1 brook brook 38 Mar 25 14:53 initramfs -rw-r--r--. 1 brook brook 35121 Mar 25 14:53 LICENSE -rw-r--r--. 1 brook brook 7587 Mar 25 14:55 Makefile drwxr-xr-x. 1 brook brook 132 Mar 25 14:53 manpages -rw-r--r--. 1 brook brook 15294 Mar 25 14:53 README.md drwxr-xr-x. 1 brook brook 50 Mar 25 14:53 temp
85% 14:53:54 USER: brook HOST: Ultramarine-16ITH6 on master grub-btrfs ❯$ vim MakefileIt is preferable to specify the value of the prefix variable to /usr/local instead of /usr to better conform to filesystem hierarchy standards. Change
PREFIX ?= /usrto
PREFIX ?= /usr/local
#GRUB_BTRFS_GRUB_DIRNAME="/boot/grub2becomes
GRUB_BTRFS_GRUB_DIRNAME="/boot/grub2
#GRUB-BTRFS_GBTRFS_DIRNAME="/boot/grub"becomes
GRUB-BTRFS_GBTRFS_DIRNAME="/boot/grub2"
#GRUB_BTRFS_MKCONFIG=/usr/bin/grub2-mkconfigbecomes
GRUB_BTRFS_MKCONFIG=/usr/sbin/grub2-mkconfig
#GRUB_BTRFS_SCRIPT_CHECK=grub2-script-checkbecomes
GRUB_BTRFS_SCRIPT_CHECK=grub2-script-check
15:26:35 USER: brook HOST: Ultramarine-16ITH6 on master [!] PCD: 1m39s grub-btrfs ❯$ sudo make installThe command with its output:
15:26:35 USER: brook HOST: Ultramarine-16ITH6 on master [!] PCD: 1m39s grub-btrfs ❯$ sudo make install [sudo] password for brook: Installing :::::::: ::::::::: ::: ::: ::::::::: ::::::::: ::::::::::: ::::::::: :::::::::: :::::::: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ :#: +#++:++#: +#+ +:+ +#++:++#+ +#++:++#++:++ +#++:++#+ +#+ +#++:++#: :#::+::# +#++:++#++ +#+ +#+# +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# ######## ### ### ######## ######### ######### ### ### ### ### ######## For further information visit https://github.com/Antynea/grub-btrfs or read the man page: 'man grub-btrfs' Installing manpages... Installing systemd .service file Installing docs... Updating the GRUB menu... Generating grub configuration file ... Found linux image: /boot/vmlinuz-6.7.9-200.fc39.x86_64 Found initrd image: /boot/initramfs-6.7.9-200.fc39.x86_64.img fgrep: warning: fgrep is obsolescent; using grep -F Found linux image: /boot/vmlinuz-6.6.8-200.fc39.x86_64 Found initrd image: /boot/initramfs-6.6.8-200.fc39.x86_64.img fgrep: warning: fgrep is obsolescent; using grep -F Found linux image: /boot/vmlinuz-0-rescue-a6c93ae8fb4347699af7cd22cd207c55 Found initrd image: /boot/initramfs-0-rescue-a6c93ae8fb4347699af7cd22cd207c55.img fgrep: warning: fgrep is obsolescent; using grep -F Found Windows Boot Manager on /dev/nvme1n1p1@/EFI/Microsoft/Boot/bootmgfw.efi Found openSUSE Tumbleweed on /dev/nvme1n1p5 Found Arch Linux on /dev/nvme1n1p6 Found Ubuntu 23.10 (23.10) on /dev/nvme1n1p7 Found Garuda Linux on /dev/nvme1n1p9 Adding boot menu entry for UEFI Firmware Settings ... Detecting snapshots ... Found snapshot: 2024-03-25 15:11:46 | @-0/.snapshots/7/snapshot | post | /usr/bin/dnf upgrade | Found snapshot: 2024-03-25 15:11:25 | @-0/.snapshots/6/snapshot | pre | /usr/bin/dnf upgrade | Found snapshot: 2024-03-25 15:05:59 | @-0/.snapshots/5/snapshot | post | /usr/bin/dnf install kdenlive | Found snapshot: 2024-03-25 15:05:51 | @-0/.snapshots/4/snapshot | pre | /usr/bin/dnf install kdenlive | Found snapshot: 2024-03-25 15:01:20 | @-0/.snapshots/3/snapshot | post | /usr/bin/dnf install kontact | Found snapshot: 2024-03-25 15:01:02 | @-0/.snapshots/2/snapshot | pre | /usr/bin/dnf install kontact | Found 6 snapshot(s) WARNING: '/usr/sbin/grub2-mkconfig' needs to run at least once to generate the snapshots (sub)menu entry in grub the main menu. After that this script can run alone to generate the snapshot entries. Unmount /tmp/grub-btrfs.MQ4d9WlZKp .. Success Done
15:28:13 USER: brook HOST: Ultramarine-16ITH6 on master [!] PCD: 55s grub-btrfs ❯$ sudo grub2-mkconfig -o /boot/grub2/grub.cfgThe command with its output:
15:28:13 USER: brook HOST: Ultramarine-16ITH6 on master [!] PCD: 55s grub-btrfs ❯$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg Generating grub configuration file ... Found linux image: /boot/vmlinuz-6.7.9-200.fc39.x86_64 Found initrd image: /boot/initramfs-6.7.9-200.fc39.x86_64.img fgrep: warning: fgrep is obsolescent; using grep -F Found linux image: /boot/vmlinuz-6.6.8-200.fc39.x86_64 Found initrd image: /boot/initramfs-6.6.8-200.fc39.x86_64.img fgrep: warning: fgrep is obsolescent; using grep -F Found linux image: /boot/vmlinuz-0-rescue-a6c93ae8fb4347699af7cd22cd207c55 Found initrd image: /boot/initramfs-0-rescue-a6c93ae8fb4347699af7cd22cd207c55.img fgrep: warning: fgrep is obsolescent; using grep -F Found Windows Boot Manager on /dev/nvme1n1p1@/EFI/Microsoft/Boot/bootmgfw.efi Found openSUSE Tumbleweed on /dev/nvme1n1p5 Found Arch Linux on /dev/nvme1n1p6 Found Ubuntu 23.10 (23.10) on /dev/nvme1n1p7 Found Garuda Linux on /dev/nvme1n1p9 Adding boot menu entry for UEFI Firmware Settings ... Detecting snapshots ... Found snapshot: 2024-03-25 15:11:46 | @-0/.snapshots/7/snapshot | post | /usr/bin/dnf upgrade | Found snapshot: 2024-03-25 15:11:25 | @-0/.snapshots/6/snapshot | pre | /usr/bin/dnf upgrade | Found snapshot: 2024-03-25 15:05:59 | @-0/.snapshots/5/snapshot | post | /usr/bin/dnf install kdenlive | Found snapshot: 2024-03-25 15:05:51 | @-0/.snapshots/4/snapshot | pre | /usr/bin/dnf install kdenlive | Found snapshot: 2024-03-25 15:01:20 | @-0/.snapshots/3/snapshot | post | /usr/bin/dnf install kontact | Found snapshot: 2024-03-25 15:01:02 | @-0/.snapshots/2/snapshot | pre | /usr/bin/dnf install kontact | Found 6 snapshot(s) Unmount /tmp/grub-btrfs.XW3rkhHVc0 .. Success done
100% 16:07:14 USER: brook HOST: Ultramarine-16ITH6 ~ ❯$ sudo systemctl enable grub-btrfsd.service Created symlink /etc/systemd/system/multi-user.target.wants/grub-btrfsd.service → /usr/local/lib/systemd/system/grub-btrfsd.service.
Except for a few minor details regarding the automating maintenance of the Btrfs and automating the cleanup of old snapshots, our configuration of a Fedora system with a Btrfs subvolume layout in the openSUSE style with Snapper configured to take advantage of all of its capabilities is complete. The normal GRUB menu items, even after rollbacks, will boot into the correct snapshot subvolume. For initiating rollbacks a new GRUB submenu is available that will allow us to select a read-only snapshot to boot into. The final state of the GRUB menu is shown in the following set of images.
At this point our Fedora system is configured with a Btrfs subvolume layout in the openSUSE style with Snapper fully integrated for managing snapshots. One of the snapshots in the subvolume /@-0/.snapshots/, the one set as the default subvolume will always be automatically mounted as the root file system. We have set the default subvolume to be the one we created and to which we moved the contents of the subvolume created by Anaconda during installation, /@-0, with the exception of filesystem hierarchy paths that should be excluded from snapshots; these have been moved to their own subvolumes. The GRUB menu now includes a menu item which can be expanded to show a submenu of available snapshots into which we can boot to initiate a rollback. Snapshots are automatically created during package management transactions and are automatically added to the GRUB menu.
The only remaining tasks are minor ones: we can refine the Snapper configuration of snapshot management of our root filesystem, in the file /etc/snapper/configs/root where we can specify the frequency of automatic snapshot cleanup and creation, apart from those created during DNF package management transactions; and we can automate the maintenance of the Btrfs filesystem by enabling services to periodically scrub and balance the filesystem. Both of these final configuration tasks can be easily performed by Btrfs Assistant, a GUI which can be used to manage the Snapper and some aspects of the filesystem. The installation and use of Btrfs Assistant to modify the relevant settings and enable the relevant services is described below.
100% 17:28:32 USER: brook HOST: Ultramarine-16ITH6 ~ ❯$ sudo dnf install btrfs-assistant Last metadata expiration check: 1:40:35 ago on Tue 26 Mar 2024 03:51:10 PM EDT. Dependencies resolved. ============================================================================================================================ Package Architecture Version Repository Size ============================================================================================================================ Installing: btrfs-assistant x86_64 1.9-1.fc39 updates 180 k Installing dependencies: libbtrfsutil x86_64 6.7.1-1.fc39 updates 32 k Installing weak dependencies: btrfsmaintenance noarch 0.5-8.fc39 fedora 34 k Transaction Summary ============================================================================================================================ Install 3 Packages Total download size: 246 k Installed size: 633 k Is this ok [Y/n]: Downloading Packages: (1/3): libbtrfsutil-6.7.1-1.fc39.x86_64.rpm 155 kB/s | 32 kB 00:00 (2/3): btrfsmaintenance-0.5-8.fc39.noarch.rpm 115 kB/s | 34 kB 00:00 (3/3): btrfs-assistant-1.9-1.fc39.x86_64.rpm 514 kB/s | 180 kB 00:00 ---------------------------------------------------------------------------------------------------------------------------- Total 252 kB/s | 246 kB 00:00 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : libbtrfsutil-6.7.1-1.fc39.x86_64 1/3 Installing : btrfsmaintenance-0.5-8.fc39.noarch 2/3 Running scriptlet: btrfsmaintenance-0.5-8.fc39.noarch 2/3 Installing : btrfs-assistant-1.9-1.fc39.x86_64 3/3 Running scriptlet: btrfs-assistant-1.9-1.fc39.x86_64 3/3 Verifying : btrfsmaintenance-0.5-8.fc39.noarch 1/3 Verifying : btrfs-assistant-1.9-1.fc39.x86_64 2/3 Verifying : libbtrfsutil-6.7.1-1.fc39.x86_64 3/3 Installed: btrfs-assistant-1.9-1.fc39.x86_64 btrfsmaintenance-0.5-8.fc39.noarch libbtrfsutil-6.7.1-1.fc39.x86_64 Complete!
The timeline cleanup settings and the Btrfs filesystem balancing settings are especially important in that they will help prevent the possibility of suddenly running out of storage space or otherwise making the system unusable. The proper setting of the timeline cleanup will ensure that an excessive amount of old and unuseful snapshots are not needlessly taking storage space. The Btrfs balancing operation is important in avoiding the case where the storage space allocated for the internal operation of the filesystem is not exhausted (see Not Enough Space on Btrfs Filesystem Due to Exhausted Metadata Block Group Allocation fore more on this particular problem).
In the section above, Btrfs-Assistant was installed and used to show how it can be used to modify the Snapper configuration and enable automatic periodic and boot-time snapshot creation, as well as automatic snapshot cleanup. It was also used to enable automated Btrfs filesystem maintenance. All of these tasks could have been done, instead, manually as shown in An Arch Linux Installation on a Btrfs Filesystem with Snapper for System Snapshots and Rollbacks.
The application is very easy to use making rollbacks simple and transparent to the user. However, it does not really use Snapper to perform rollbacks. Because of the operational differences in how rollbacks are effected, it is not for users who want to do things the openSUSE way.
Each time Snapper is used to perform a system rollback, it creates two new snapshots, one a read-only duplicate of the current system (the snapshot subvolume mounted at /) and another that is a read-write copy of the snapshot to which to roll back. The new read-write snapshot is then set as the default subvolume, ensuring the correct snapshot is mounted at /.Btrfs-Assistant on the other hand uses Btrfs provided utility commands to perform the rollback. It first copies the current system (the subvolume currently mounted at /) to a new subvolume as a backup, renames the current subvoolume mounted at /, copies the subvolume to wich to rollback and renames it to the same name as the subvolume which is to be rolled back.
For users who want to make full use of Snapper functionality, rollbacks should be performed one of two ways. The first as described in openSUSE documentation, where the snapshot to which to rollback is selected from available snapshots in the GRUB menu, then a Snapper rollback command issued. The secoond alternative method is to simply issue a rollback command in the current system (the system to be rolled back) specifying the snapshot to which to rollback. This is illustrated in the following image.
The topmost Konsole window shows the state of the system before the rollback, in the first command output, the available snapshots to which we could rollback; in the second output, the curent default Btrfs subvolume, /@-0/.snapshots/1/snapshot, which is still the initial snapshot subvolume, since, at this point no rollbacks have been made since installation, and in the third command output, the kernel command line showing that the kernel path is in the filesystem hierarchy contained in /@-0/.snapshots/1/snapshot.
The middle window shows the current GRUB configuration in /boot/grub2/grub.cfg. The command to load the kernel image and the command to load the initial RAM disk both point to paths under the initial snapshot subvolume.
The lowermost Konsole shows the rollback command used:
snapper --ambit classic rollback -p -d "< a description of the rollback >" 189
and its output. The last argument in the command is the snapshot to which to rollback. The output indicates that the two new snapshots as described earlier are created, with the snapshot numbers indicated. The output also indicates that the read-write snapshot is set as the new default subvolume and provides its snapshot number,, in this case 191.
After the rollback command successfully completed, the system is rebooted, but the default GRUB entry kernel and initrd loading commands must be edited to reflect the new path under the new default subvolum, i.e., /@-0/.snapshots/1/snapshot must be modified to /@-0/.snapshots/191/snapshot. This will only have to be done once. When GRUB executes, it will load the kernel and initrd images from new system snapshot subvolume and /etc/fstab will automatically mount the new system snapshot subvolume at / as described earlier. After the boot GRUB is updated so that the GRUB configuration will have the new paths of the kernel and initrd image under the new system snapshot subvolume.
The state of the system after the rollback is shown in the following image. The larger, leftmost Konsole window shows the outputs of three commands that show the snapshot subvolume that is the system root after the rollback. The first shows the list of snapshots, with the asterisk (*) next to snapshot number 191 indicating that it is the currently mounted snapshot and the one to be mounted at next boot, whereas before the rollback it had been snapshot number 1. The second output shows that the kernel image path is under /@-0/.snapshots/191/snapshot, whereas before it had been at /@-0/.snapshots/1/snapshot. And lastly, the third output indicates that the current default subvolume for the filesystem is /@-0/.snapshots/191/snapshot, as set by Snapper during the rollback, changing it from the prior default subvolume, /@-0/.snapshots/1/snapshot.
The Konsole window at the top right side of the image show the GRUB configuration immediately after rebooting into the rollbacked system. At this point the entry still points to the previous system snapshot subvolume, /@-0/.snapshots/1/snapshot, for the kernel and intird image paths. In the middle window, the end of the output of the grub configuration generation command is displayed, the part where grub-btrfs lists the found snapshots. The third window shows the default GRUB entry after GRUB is updated. After updating the GRUB configuration with grub2-mkconfig the kernel and initrd paths now reflect the new system snapshot subvolume, /@-0/.snapshots/191/snapshot.
This article presented a process which converted an initial Fedora 39 (Ultramarine Linux 39) installation on a Btrfs filesystem with a simple subvolume layout to one with the hierarchical subvolume layout of openSUSE. Snapper was installed and configured to manage snapshots and rollbacks, as well as a DNF plugin to automate creation of snapshots during package management transactions. grub-btrfs was also installed for adding a submenu to the GRUB menu with a list of available snapshots into which the system can be booted to initiate a rollback to that snapshot, as documented by openSUSE.
Btrfs-Assistant was also introduced to illustrate other automatics snapshot creation and cleanup mechanisms, apart from those employed during package management. But for users who go to the trouble of using this process to convert a Fedora Btrfs installation to the openSUSE style of Btrfs subvolume layout, use of this utility to rollback the system may not be desireable as the functionality is different from a Snapper rollback, as described in the article.
It should be noted that, while the hierarchical subvolume layout is like openSUSE's in that the hierarchy is the same, openSUSE also excludes the path which contains the generated GRUB configuration, /boot/grub (or /boot/grub2 as it would be in Fedora) by putting it in a separate subvolume at the same level as the other subvolumes used to exclude certain paths of the filesystem hierarchy.
The system configured by the process described in this article survives a release upgrade (see Release Upgrade of a Fedora 39 Installation on a Btrfs Filesystem with an openSUSE Style Subvolume Layout to Fedora 40, requiring only a few post-upgrade tasks to reinstall GRUB because of the nonstandard use of GRUB in Fedora, e.g., the GRUB configuration stub that is written to the ESP.
A reboot at this point in the process should result in the Fedora (or in this case, the Ultramarine) GRUB menu being displayed. This may not happen for various reasons, from interference with the platform security features and Secure Boot, excessive manipulation of UEFI NVRAM variables with efibootmgr, and excessive manipulation of the EFI partition. In my case, for these reasons, in the course of experimenting to work out the process presented in this article, when rebooting at this stage, I was presented with either a GRUB shell, a boot that stopped immediately after the message XXX was echoed by GRUB, or an error indicating a platform security violation.
If presented with a GRUB shell when rebooting at this point in the process, we can boot the installed system from the shell, reinstall the GRUB firmware image, and update the GRUB configuration.
grub> lsThis will put a space separated list with elements such as (hd1,gpt8). The first component of this couple identifies the disk and the second component identifies the partition. In this case (hd1,gpt8) represents the eighth partition of a GPT partitioned disk on the second recognized disk.
grub> set root=(hd1,gpt8)In the system used as the reference for this article, the installation of the root partition, where the Btrfs volume named ULTRAMARINE_ROOT was created, was on the eight partition of the second disk.
grub> linux /@-0/.snapshots/1/snapshot/boot/vm[TAB]will list the possible completions as shown in the image, above. Enter the full command line:
grub >linux /@-0/.snapshots/1/snapshot/boot/vmlinuz-6.7.9-200.fc39.x86_64 root=/dev/nvme1n1p8The path to the kernel is specified including the subvolume path; the block device which identifies the disk and partition is also specified as the value of the parameter root.
grub> initrd /@-0/.snapshots/1/snapshot/boot/i[TAB}will list the available initial RAM disk files. Enter the full command, specifying an initrd with a version that matches the kernel version specified in the previous step.
grub> initrd /@-0/.snapshots/1/snapshot/boot/initramfs-6.7.9-200.fc39.x86_64.img
grub> bootThe Fedora installation should boot as normal.
sudo dnf reinstall grub2-efi grub2-efi-modules shimThis will write the GRUB firmware application in the Fedora ESP/EFI directory and write the appropriate values to the UEFI NVRAM that point to the GRUB firmware application in the Fedora ESP/EFI directory.
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
If presented with an error indicating a problem with platform security or a platform security policy violation, all that is required is to disable secure boot and/or reset the security keys.
GRUB written to ESP but firmware NVRAM has not been updated.
If the computer that is the target of the installation has a firmware interface that allows registering a firmware application that the interface can access, it can be used to register the application to the NVRAM. One of the laptops used as the reference for this article, the Dell G5 has a very advanced firmware interface in which an entry could be created for the platform firmware boot manager. Two of the relevant screens for this process are shown in the following set of images.
Another option, is to boot into the live ISO environment, chroot into the isntalled system and repair GRUB. The process below will re-write the GRUB firmware application to the ESP and write variable values to the firmware NVRAM that point to the firmware application in the ESP.
Use the relevant methods, described above, to either regenerate the GRUB configuration, or reinstall the grub* and shim packages and regenerate the GRUB configuration.