In this post, I will show you how to install Fedora 36 with LUKS Full Disk Encryption (FDE) with snapper, and grub-btrfs, enabling you to do a full system rollback. In this setup, the /boot partition is included in the root file system, enabling you to make a full system rollback including kernels.
Encrypting your laptop or computer's drive with LUKS Full Disk Encryption protects the data on it even if the disk is physically removed from the system. To access the decrypted content on the device, you need to provide a passphrase or key file.
Apart from GNOME 42, another major change in Fedora 36 is the relocation of the RPM database from /var/lib/rpm to /usr/lib/sysimage/rpm. This is an important step towards full support for snapshot and rollback regimes, much like OpenSUSE Linux. Because the RPM database is now stored in /usr, the /usr may be rolled back as a unit without affecting /var. This will prevent any failed or undesired updates of the operating system.
Here's a quick rundown of what to anticipate.
- Install Fedora 36 Workstation with a LUKS encrypted BTRFS ‘root file system’ and a separate un-encrypted EXT4 /boot partition, as the Anaconda installer does not support encryption of the /boot partition.
- Move the contents of the /boot partition to the ‘root file system,’ and configure GRUB to boot from it.
- Remove the old /boot partition and merge the space it used into the main BTRFS volume.
- Configure the system to ask for the passphrase only once by providing the key file.
- Install and configure snapper to take snapshots of the root file system.
- Install and configure grub-btrfs so that you are able to boot from snapshots using the GRUB menu.
- Finally, test the 'snapper undochange' and 'snapper rollback' commands to make sure everything is working correctly.
If you prefer to install Fedora 36 without LUKS Full Disk Encryption, please visit my other article ‘How to Install Fedora 36 with Snapper and Grub-Btrfs‘.
So let’s begin.
Table of Contents
- 1. Disk Layout for Installing Fedora 36 Workstation
- 2. Install Fedora 36 Workstation
- 3. Move the /boot to the 'root file system'
- 4. Delete the /dev/sda3 Partition and Reclaim the Space
- 5. Bypass the Additional Passphrase Prompt
- 6. Install and Configure snapper for the 'root file system'
- 7. Setup Snapshot Booting from the GRUB Menu
- 8. Create Snapshots and Test the Undochange and Rollback Features
- 9. Conclusion
- Watch on YouTube
1. Disk Layout for Installing Fedora 36 Workstation
I'll be using an 80 GiB hard disk. Because the Fedora Anaconda installer does not allow encryption of the /boot partition, either separately or within the root file system, you must first create a non-encrypted /boot partition separately. This is how the disk partition appears at first.
NAME SIZE FSTYPE LABEL MOUNTPOINT /dev/sda 80G ├─/dev/sda1 512M vfat EFI /boot/efi ├─/dev/sda2 78G btrfs(LUKS) FEDORA / └─/dev/sda3 1.5G ext4 BOOT /boot
Once the installation is finished, the /boot partition is moved to the root file system, and the final disk partition looks like this.
NAME SIZE FSTYPE LABEL MOUNTPOINT /dev/sda 80G ├─/dev/sda1 512M vfat EFI /boot/efi └─/dev/sda2 79.5G btrfs(LUKS) FEDORA /
And these are the subvolumes created from the btrfs volume (/dev/sda2).
NAME MOUNTPOINT TYPE [main] / mainvolume home /home subvolume opt /opt subvolume tmp /tmp subvolume var /var subvolume usr-local /usr/local subvolume snapshots /.snapshots subvolume
Subvolumes created to exclude directories from snapshots:
/home
Contains the user's data. It is excluded to avoid data loss on rollbacks.
/opt
Third-party products usually get installed here. It is excluded to avoid applications installed in /opt from being uninstalled during rollbacks.
/tmp
The directory contains temporary files that you do not need when taking a snapshot.
/var
This directory holds various variable data, such as logs, and temporary caches. It is also the default location for virtual machine images and databases. Therefore, this subvolume is created to exclude all of this variable data. Copy-On-Write will also be disabled on this directory.
/usr/local
Contains software that was manually installed. It is excluded to prevent these installations from being uninstalled during rollbacks.
2. Install Fedora 36 Workstation
Boot your system using the Fedora 36 Workstation installer in UEFI mode. On the welcome screen, select the Install to Hard Drive option. Next, select your Language, Keyboard, and configure Time & Date. Following that, from the INSTALLATION SUMMARY screen, select Installation Destination.
You should now be on the INSTALLATION DESTINATION screen. To proceed, pick the Advanced Custom (Blivet-GUI) radio button and then hit the Done button.
You will see the BLIVET GUI PARTITIONING screen. Here you will create partitions, file systems, and btrfs subvolumes necessary to install Fedora 36 Workstation.
First, you need to create and mount the EFI partition. Select the free space and click the + sign to create a partition.
Set the partition Size to 512 MiB, the Filesystem to EFI System Partition, and the Mountpoint to /boot/efi.
Then, you need to create a btrfs volume where you can create all the subvolumes needed to install Fedora Linux.
Select the free space again and click on the + sign to create a Btrfs volume. (1) Set the Device Type to Btrfs Volume. (2) Set the Size of the Btrfs volume. At the end, leave at least 1 GiB for the /boot partition. I’ve left around 1.5 GiB unallocated. (3) Set the name for Btrfs volume. I named the Btrfs volume FEDORA, but you can name it whatever you like. (4) Set the Mountpoint to /, (5) Set the Encryption type to luks1, and (6) provide a strong password (password entropy > 60 bits is suggested). Finally, click on the OK button.
Since support for LUKS2 in GRUB 2.0.6 is limited (Argon2 is not yet supported, only the PBKDF2 is supported), it is recommended to use LUKS1 to encrypt the disk if the /boot directory is in the ‘root file system’ and not on a separate partition. Hopefully, in the next release of GRUB, support for /boot partition encryption using LUKS2 with Argon2i and Argon2id will be added.
You then need to create and mount the /boot partition. Set the boot partition to use all of the remaining unallocated space (1534 MiB), the Filesystem to ext4, and the Mountpoint to /boot. After the installation is finished, I’ll move the contents of the /boot partition to the main root file system and merge this 1.5 GiB space with the main BTRFS volume.
Next, you must create subvolumes. (1) Select the Btrfs Volume from the left panel, and (2) click on the + sign on the right panel.
Subvolumes, unlike standard partitions or LVM logical volumes, do not have a size; instead, they behave like directories with shared space. However, the resemblance of subvolumes to directories ends here. Each subvolume, like a file system, has its own file tree, has its own POSIX namespace, and has its own pool of inodes. This means that hard links cannot be created between the subvolumes. From this point of view, a subvolume begins to look more like a separate file system. Subvolumes are not block devices either.
The Btrfs subvolumes are created in reverse order in the Anaconda installer for some reason. It means that the first specified subvolume is created last, and the last specified subvolume is created first. I will therefore specify the subvolumes in reverse order.
Create snapshots subvolume. Enter the Name as snapshots and Mountpoint as /.snapshots. Click OK to finish.
Repeat the same process for the other subvolumes. I created other subvolumes in the following order: usr-local (/usr/local), var (/var), tmp (/tmp), opt (/opt), and lastly home (/home).
When completed, the subvolumes must look something like this. Click Done to create subvolumes.
As with the swap partition, Fedora generates a SwapOnZRAM upon startup, so no separate swap partition is needed.
Verify that the partitions and subvolumes are properly defined on the SUMMARY OF CHANGES screen. To finalize the changes, click the Accept Changes button.
You will be returned to the INSTALLATION SUMMARY screen. Press the Begin Installation button to start the installation process. The installation process will start. Just sit back and relax.
When the installation is finished, click the Finish installation button and restart the system. After the computer restarts, you will be prompted for the LUKS passphrase.
Enter the LUKS passphrase that you provided. The last phase of the installation procedure will start. Click the Start Setup button to complete the remaining customization steps, such as setting a new login, password, and so on.
You will then be logged into the Fedora 36 Workstation with the all-new Gnome 42 desktop interface.
Now, open the Gnome terminal and check your current setup.
List the subvolumes.
# btrfs subvolume list /
ID 256 gen 80 top level 5 path home
ID 257 gen 44 top level 5 path opt
ID 258 gen 80 top level 5 path tmp
ID 259 gen 80 top level 5 path var
ID 260 gen 70 top level 5 path usr-local
ID 261 gen 19 top level 5 path snapshots
ID 262 gen 77 top level 259 path var/lib/machines
Show the BTRFS filesystem.
# btrfs filesystem show /
Label: 'FEDORA' uuid: e03e52c7-1d69-4f72-b674-5116352637a3
Total devices 1 FS bytes used 6.57GiB
devid 1 size 78.00GiB used 8.02GiB path /dev/mapper/luks-6bb9a37b-e6f3-4e21-aeb9-841891d3cd71
List block devices.
# lsblk -pf
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
/dev/sda
├─/dev/sda1 vfat FAT32 EFI 818A-7CD7 497.1M 3% /boot/efi
├─/dev/sda2 crypto_LUKS 1 6bb9a37b-e6f3-4e21-aeb9-841891d3cd71
│ └─/dev/mapper/luks-6bb9a37b-e6f3-4e21-aeb9-841891d3cd71 btrfs FEDORA e03e52c7-1d69-4f72-b674-5116352637a3 70.6G 8% /var
│ /usr/local
│ /tmp
│ /.snapshots
│ /opt
│ /home
│ /
└─/dev/sda3 ext4 1.0 BOOT 2ecae8c7-8cd4-48e9-9faa-dc48d590fbbc 1.2G 10% /boot
/dev/zram0 [SWAP]
Review /etc/fstab file.
# cat /etc/fstab
UUID=e03e52c7-1d69-4f72-b674-5116352637a3 / btrfs defaults,x-systemd.device-timeout=0 0 0
UUID=e03e52c7-1d69-4f72-b674-5116352637a3 /.snapshots btrfs subvol=snapshots,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=2ecae8c7-8cd4-48e9-9faa-dc48d590fbbc /boot ext4 defaults 1 2
UUID=818A-7CD7 /boot/efi vfat umask=0077,shortname=winnt 0 2
UUID=e03e52c7-1d69-4f72-b674-5116352637a3 /home btrfs subvol=home,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=e03e52c7-1d69-4f72-b674-5116352637a3 /opt btrfs subvol=opt,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=e03e52c7-1d69-4f72-b674-5116352637a3 /tmp btrfs subvol=tmp,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=e03e52c7-1d69-4f72-b674-5116352637a3 /usr/local btrfs subvol=usr-local,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=e03e52c7-1d69-4f72-b674-5116352637a3 /var btrfs subvol=var,compress=zstd:1,x-systemd.device-timeout=0 0 0
Review the device LUKS encryption details.
# cryptsetup luksDump /dev/sda2
LUKS header information for /dev/sda2
Version: 1
Cipher name: aes
Cipher mode: xts-plain64
Hash spec: sha256
Payload offset: 4096
MK bits: 512
MK digest: 77 ea cc 07 89 01 a1 11 be 48 a8 4f b3 50 c4 6a b6 c0 bf 80
MK salt: 62 39 cf ea 60 73 48 0d 9a 8e 95 7b b3 85 f9 71
d2 fb 9b e7 27 24 51 41 77 56 07 97 1e bf 70 22
MK iterations: 86118
UUID: 6bb9a37b-e6f3-4e21-aeb9-841891d3cd71
Key Slot 0: ENABLED
Iterations: 1376082
Salt: e1 b3 30 fb 23 b9 fa e5 a4 c4 4b b1 7e e6 f1 67
36 8d d0 51 a0 49 7a d2 5d 7d 81 02 11 36 42 f7
Key material offset: 8
AF stripes: 4000
Key Slot 1: DISABLED
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED
Because of the Copy-On-Write feature of BTRFS, the performance of qcow2 images and databases suffers when they are on a BTRFS subvolume. Therefore, you need to disable the Copy-On-Write function on the /var directory.
# chattr -R -f +C /var
# lsattr -d /var
---------------C------ /var
3. Move the /boot to the 'root file system'
Now that the installation is complete, transfer the contents of the /boot partition to the 'root file system', and configure GRUB to boot from it.
Unmount the /boot mount recursively.
# umount -vR /boot
umount: /boot/efi unmounted
umount: /boot unmounted
Now mount the /dev/sda3 device into the /mnt directory temporarily.
# mount -v /dev/sda3 /mnt
mount: /dev/sda3 mounted on /mnt.
Copy all boot files from /mnt to the /boot directory.
# cp -arv /mnt/. /boot
Unmount the /dev/sda3 partition from /mnt and delete its entry from the /etc/fstab file. You no longer need the /dev/sda3 partition.
# umount -v /mnt
# sed -i.original '/\/boot.*ext4/d' /etc/fstab
# systemctl daemon-reload
# mount -a
Restore the SELinux labels.
# restorecon -RFv /boot
Enable the CRYPTODISK option in GRUB. When enabled, it will check the encrypted disks and generate the additional commands needed to access them during boot.
# echo "GRUB_ENABLE_CRYPTODISK=y" >> /etc/default/grub
# grub2-mkconfig -o /boot/grub2/grub.cfg
The kernel and initrd paths are configured to look in the parent directory of the previous /boot partition (/dev/sda3). This must be changed to the /boot directory in the ‘root file system’ (/dev/sda2).
# grubby --info=DEFAULT
index=0
kernel="/vmlinuz-5.17.5-300.fc36.x86_64"
args="ro rd.luks.uuid=luks-6bb9a37b-e6f3-4e21-aeb9-841891d3cd71 rhgb quiet"
root="UUID=e03e52c7-1d69-4f72-b674-5116352637a3"
initrd="/initramfs-5.17.5-300.fc36.x86_64.img"
title="Fedora Linux (5.17.5-300.fc36.x86_64) 36 (Workstation Edition)"
id="aa65c3ac5d924efba3b0301f2a0cd167-5.17.5-300.fc36.x86_64"
To do this, first, remove the old Boot Loader Specification (BLS) configuration files.
# rm /boot/loader/entries/*.conf
Then recreate the BLS config file by adding the kernel image.
# kernel-install -v add $(uname -r) /lib/modules/$(uname -r)/vmlinuz
Examine the newly created BLS configuration files. The ‘/boot‘ directory should be added to the kernel and initrd lines.
# grubby --info=DEFAULT
index=0
kernel="/boot/vmlinuz-5.17.5-300.fc36.x86_64"
args="ro rd.luks.uuid=luks-6bb9a37b-e6f3-4e21-aeb9-841891d3cd71 rhgb quiet"
root="UUID=e03e52c7-1d69-4f72-b674-5116352637a3"
initrd="/boot/initramfs-5.17.5-300.fc36.x86_64.img"
title="Fedora Linux (5.17.5-300.fc36.x86_64) 36 (Workstation Edition)"
id="aa65c3ac5d924efba3b0301f2a0cd167-5.17.5-300.fc36.x86_64"
As CRYPTODISK is now enabled, you must make changes to /boot/efi/EFI/fedora/grub.cfg file as well.
Setup access to the encrypted boot device.
# CRYPTOUUID="$(grub2-probe --target=cryptodisk_uuid /)"
# sed -i.backup1 "1i cryptomount -u $CRYPTOUUID" /boot/efi/EFI/fedora/grub.cfg
Replace the old /dev/sda3 device UUID with a LUKS encrypted device UUID where the root file system is mounted.
# OLDBOOTUUID="$(lsblk -dno uuid /dev/sda3)"
# NEWLUKSUUID="$(lsblk -dno uuid /dev/mapper/luks-$(cryptsetup luksUUID /dev/sda2))"
# sed -i.backup2 "s/$OLDBOOTUUID/$NEWLUKSUUID/" /boot/efi/EFI/fedora/grub.cfg
Finally, add /boot to the grub2 path.
# sed -i.backup3 's#($dev)/grub2#($dev)/boot/grub2#' /boot/efi/EFI/fedora/grub.cfg
In the end, your /boot/efi/EFI/fedora/grub.cfg file should look like this.
# cat /boot/efi/EFI/fedora/grub.cfg
cryptomount -u 6bb9a37be6f34e21aeb9841891d3cd71
search --no-floppy --fs-uuid --set=dev e03e52c7-1d69-4f72-b674-5116352637a3
set prefix=($dev)/boot/grub2
export $prefix
configfile $prefix/grub.cfg
The changes are as follows.
- Setup access to the encrypted boot device. The UUID here is of the /dev/sda2 partition without dashes.
- Change the UUID to the mapped LUKS1 device that contains the root file system.
- Insert the '/boot' directory into the ‘set prefix‘ line.
Reboot the computer now to ensure that everything is working correctly. You will be prompted for the passphrase twice. The first time is before loading GRUB, and the second time is before mounting the root file system. Depending on your computer's CPU, it may take up to 30 seconds for the GRUB to display the second passphrase prompt. So please be patient.
4. Delete the /dev/sda3 Partition and Reclaim the Space
You no longer need the /dev/sda3 partition since you moved the boot files to the /boot directory in the root file system. As a result, you may safely delete the /dev/sda3 partition from the partition table and move the 1.5 GiB it occupies to the BTRFS volume (/dev/sda2).
Run the gdisk
utility.
# gdisk /dev/sda
On the command input prompt, enter the key p
to print the partition table.
Command (? for help): p
Disk /dev/sda: 167772160 sectors, 80.0 GiB
Model: WD800AAJS
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 748465AC-93DB-4F42-B799-499466E5D0C5
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 167772126
Partitions will be aligned on 2048-sector boundaries
Total free space is 4029 sectors (2.0 MiB)
Number Start (sector) End (sector) Size Code Name
1 2048 1050623 512.0 MiB EF00 EFI System Partition
2 1050624 164628479 78.0 GiB 8300
3 164628480 167770111 1.5 GiB 8300
Delete partition #3 (/dev/sda3) by pressing the key d
and then the number 3
.
Command (? for help): d
Partition number (1-3): 3
Command (? for help): p
Disk /dev/sda: 167772160 sectors, 80.0 GiB
Model: WD800AAJS
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 748465AC-93DB-4F42-B799-499466E5D0C5
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 167772126
Partitions will be aligned on 2048-sector boundaries
Total free space is 3145661 sectors (1.5 GiB)
Number Start (sector) End (sector) Size Code Name
1 2048 1050623 512.0 MiB EF00 EFI System Partition
2 1050624 164628479 78.0 GiB 8300
Then, delete partition #2 (/dev/sda2) as well by pressing the key d
and then the number 2
. Don’t worry, until you permanently write the modifications, the data will not be lost.
Command (? for help): d
Partition number (1-2): 2
Command (? for help): p
Disk /dev/sda: 167772160 sectors, 80.0 GiB
Model: WD800AAJS
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 748465AC-93DB-4F42-B799-499466E5D0C5
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 167772126
Partitions will be aligned on 2048-sector boundaries
Total free space is 166723517 sectors (79.5 GiB)
Number Start (sector) End (sector) Size Code Name
1 2048 1050623 512.0 MiB EF00 EFI System Partition
Now, create a new partition by pressing the key n
. Choose the entire disc space by pressing the [Enter]
key four times. Don’t input any values; instead, let it use the defaults. Your data will remain intact with the new expanded size.
Command (? for help): n
Partition number (2-128, default 2):
First sector (34-167772126, default = 1050624) or {+-}size{KMGTP}:
Last sector (1050624-167772126, default = 167772126) or {+-}size{KMGTP}:
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'
Command (? for help): p
Disk /dev/sda: 167772160 sectors, 80.0 GiB
Model: WD800AAJS
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 748465AC-93DB-4F42-B799-499466E5D0C5
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 167772126
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)
Number Start (sector) End (sector) Size Code Name
1 2048 1050623 512.0 MiB EF00 EFI System Partition
2 1050624 167772126 79.5 GiB 8300 Linux filesystem
Finally, hit the w
key to permanently save the table to disk and quit.
Command (? for help): w
Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!
Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/sda.
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot or after you
run partprobe(8) or kpartx(8)
The operation has completed successfully.
For the changes to take effect, restart the system. After restarting the system, use the following command to reclaim all available space on the BTRFS volume.
# btrfs filesystem resize max /
Now check the space used up by the BTRFS volume.
# btrfs filesystem show /
Label: 'FEDORA' uuid: e03e52c7-1d69-4f72-b674-5116352637a3
Total devices 1 FS bytes used 6.69GiB
devid 1 size 79.50GiB used 8.02GiB path /dev/mapper/luks-6bb9a37b-e6f3-4e21-aeb9-841891d3cd71
List block devices to verify whether the /dev/sda3 partition has actually been removed.
# lsblk -p /dev/sda
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
/dev/sda 8:0 0 80G 0 disk
├─/dev/sda1 8:1 0 512M 0 part /boot/efi
└─/dev/sda2 8:2 0 79.5G 0 part
└─/dev/mapper/luks-6bb9a37b-e6f3-4e21-aeb9-841891d3cd71 253:0 0 79.5G 0 crypt /var
/usr/local
/tmp
/opt
/home
/.snapshots
/
5. Bypass the Additional Passphrase Prompt
While GRUB asks for a passphrase to unlock the encrypted /boot files, this information is not passed on to initramfs. As a result, during the initramfs stage, the root (/) must be unlocked once again. This means that either the user must input his passphrase twice, or the initramfs image itself must include a file with the root partition’s passphrase.
Because the initramfs image will be in the /boot directory of the encrypted root partition (/), the passphrase will be secure when the data is at rest. However, once the data is online, it must be safeguarded by making it read-only for the root user.
So let’s create a key file so that the passphrase is only asked once.
Create /etc/cryptsetup-keys.d directory if it is not already there.
# mkdir /etc/cryptsetup-keys.d
Create a key file with 4KiB of random data. The key file has to be in volume.key format.
# dd if=/dev/random \
of=/etc/cryptsetup-keys.d/luks-$(cryptsetup luksUUID /dev/sda2).key \
bs=512 count=8
Ensure that only the root user has read access to the key file.
# chmod 0400 /etc/cryptsetup-keys.d/*.key
Attach the created key to the encrypted device’s available key slot. When prompted for a passphrase, enter the normal LUKS passphrase.
# cryptsetup luksAddKey /dev/sda2 \
/etc/cryptsetup-keys.d/luks-$(cryptsetup luksUUID /dev/sda2).key
Include the key in the initramfs image as well. To do so, open (or create) the /etc/dracut.conf.d/cryptodisk.conf file.
# vim /etc/dracut.conf.d/cryptodisk.conf
And add the following line.
install_items+=" /etc/cryptsetup-keys.d/* "
Finally, rebuild the initramfs image.
# dracut -vf
Reboot the computer and verify that everything is operating correctly. The LUKS passphrase should only be asked once.
6. Install and Configure snapper for the 'root file system'
Install snapper and the optional package python3-dnf-plugin-snapper. The python3-dnf-plugin-snapper package allows you to generate pre and post snapshots every time you install a package on the system with the dnf
command-line tool.
$ sudo dnf install snapper python3-dnf-plugin-snapper
Then you must create a new snapper configuration for the BTRFS volume at '/'. Because you previously created a snapshots subvolume with ID=5 in the /.snapshots directory during Fedora installation, attempting to create a new snapper configuration for '/' now will result in an error. To work around this issue, make the following adjustments before creating a new snapper configuration for the '/'.
Remove the directory /.snapshots.
$ sudo umount /.snapshots
$ sudo rmdir /.snapshots
Now, create a new snapper configuration named root for the BTRFS volume at /.
$ sudo snapper -c root create-config /
This will create a configuration file at /etc/snapper/configs/root, a new subvolume .snapshots, and the directory /.snapshots.
Now that the snapper is configured, delete the /.snapshots subvolume.
$ sudo btrfs subvolume delete /.snapshots
This will remove the newly created subvolume .snapshots and directory /.snapshots, but the configuration file at /etc/snapper/configs/root will be kept.
Create the directory /.snapshots again.
$ sudo mkdir /.snapshots
As you have already linked the /.snapshots directory to the snapshots subvolume in the /etc/fstab file, all you have to do now is reload it.
$ sudo systemctl daemon-reload
$ sudo mount -a
Enable snapshot booting by appending the SUSE_BTRFS_SNAPSHOT_BOOTING="true" option to the /etc/default/grub file.
$ echo 'SUSE_BTRFS_SNAPSHOT_BOOTING="true"' | sudo tee -a /etc/default/grub
Update the grub.cfg file.
$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg
Because snapshot booting is now enabled, you must make modifications to the /boot/efi/EFI/fedora/grub.cfg file as well.
$ sudo sed -i '1i set btrfs_relative_path="yes"' /boot/efi/EFI/fedora/grub.cfg
You’re almost done; all that’s left is for the normal user to be able to list snapshots. Add your username to snapper's root config.
$ sudo snapper -c root set-config ALLOW_USERS=$USER SYNC_ACL=yes
And change the group permission of the /.snapshots directory.
$ sudo chown -R :$USER /.snapshots
Finally, disable the GRUB menu auto-hide feature. When Fedora is the only operating system installed on the system, Fedora implements a feature called ‘Hidden Grub Menu‘. The goal is to keep the interface as simple as possible, providing information only when it is necessary. However, you need to visually see the GRUB menu to verify whether the rollback is executed correctly.
This feature can be disabled by doing the following.
$ sudo grub2-editenv - list
saved_entry=aa65c3ac5d924efba3b0301f2a0cd167-5.17.5-300.fc36.x86_64
menu_auto_hide=1
boot_success=1
boot_indeterminate=0
$ sudo grub2-editenv - unset menu_auto_hide
The snapper configuration is now complete. Now list the snapshots.
$ snapper ls
# | Type | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+------+------+---------+-------------+---------
0 | single | | | root | | current |
At this stage, you do not have any snapshots, we will create some later.
7. Setup Snapshot Booting from the GRUB Menu
As you have already installed snapper on your system, you can rollback to any snapshot by executing snapper rollback
from the functioning system.
But what if you make a mistake and the system fails to boot properly the following time? You might wish to revert to a previous working snapshot. This is where snapshot booting from the GRUB menu comes in handy.
Thanks to Antynea, there is already a package called grub-btrfs, which you can now easily install on your system. The package adds "Fedora Linux snapshots" to the GRUB menu and allows you to boot to a snapshot in read-only mode to test it before rolling back to it in read-write mode.
So let's get grub-btrfs installed.
$ git clone https://github.com/Antynea/grub-btrfs.git
$ cd grub-btrfs
$ sudo make install
To make it work with Fedora, you must make a few adjustments to the grub-btrfs config file. So, open the config file and uncheck the lines listed below. Also, the path to grub2-mkconfig
is /usr/sbin/grub2-mkconfig, not /usr/bin/grub2-mkconfig.
$ sudo vim /etc/default/grub-btrfs/config
...
GRUB_BTRFS_SHOW_TOTAL_SNAPSHOTS_FOUND="true"
GRUB_BTRFS_GRUB_DIRNAME="/boot/grub2"
GRUB_BTRFS_MKCONFIG=/usr/sbin/grub2-mkconfig
GRUB_BTRFS_SCRIPT_CHECK=grub2-script-check
...
Update the grub.cfg file. You will receive the 'No snapshots found' error since you have not yet created any snapshots, but don't worry, it will function properly after you do.
$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg
Finally, enable the grub-btrfsd service.
$ sudo systemctl enable --now grub-btrfsd
Your grub-btrfs setup is now complete.
8. Create Snapshots and Test the Undochange and Rollback Features
I'll do two tests here. I'll start by installing a package, then run the snapper undochange
command to ensure that all modifications have been undone. Second, I will upgrade to the most current kernel, boot into it, purposely render the system unusable, and then attempt to rollback to the same kernel using the snapper rollback
command using the post snapshot of the kernel update in an operational state.
Test 1: Install a package and undo it.
I'm going to install a little package called ps_mem here for testing purposes. I'll inspect the system after it's been installed to see what changes have been done. Then I'll try to undo the changes and see if I can get the system back to where it was before I installed it.
Install the ps_mem package. It displays the core memory used per program (not per process).
$ sudo dnf install ps_mem
Run the ps_mem
program.
$ sudo ps_mem
Private + Shared = RAM used Program
144.0 KiB + 23.5 KiB = 167.5 KiB fusermount
288.0 KiB + 25.0 KiB = 313.0 KiB mcelog
388.0 KiB + 78.5 KiB = 466.5 KiB rtkit-daemon
492.0 KiB + 117.0 KiB = 609.0 KiB gnome-session-ctl
....
....
29.4 MiB + 6.9 MiB = 36.2 MiB systemd-journald
44.0 MiB + 8.0 MiB = 52.0 MiB gnome-software
191.3 MiB + 1.7 MiB = 193.0 MiB packagekitd
190.7 MiB + 27.6 MiB = 218.3 MiB gnome-shell
---------------------------------
1.0 GiB
=================================
OK, the ps_mem package has been installed successfully. Check the snapper for snapshots.
$ snapper ls
# | Type | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+----------------------------------+------+---------+-----------------------------+---------
0 | single | | | root | | current |
1 | pre | | Saturday 21 May 2022 11:47:50 AM | root | number | /usr/bin/dnf install ps_mem |
2 | post | 1 | Saturday 21 May 2022 11:47:55 AM | root | number | /usr/bin/dnf install ps_mem |
As you can see, the pre (#1) and the post (#2) snapshots have been created for the ps_mem package.
Let me now examine the changes it has made to the system, between snapshots #1 and #2.
$ snapper status 1..2
+..... /boot/grub2/grub.cfg.new
+..... /usr/bin/ps_mem
c..... /usr/lib/sysimage/rpm/rpmdb.sqlite-shm
c..... /usr/lib/sysimage/rpm/rpmdb.sqlite-wal
+..... /usr/share/doc/ps_mem
+..... /usr/share/doc/ps_mem/LICENSE
+..... /usr/share/man/man1/ps_mem.1.gz
Now I’ll undo the changes.
$ sudo snapper undochange 1..2
create:0 modify:2 delete:5
Check to see if the package ps_mem was indeed removed.
$ sudo ps_mem
sudo: ps_mem: command not found
Cool! The undo has been successful. But, because I want to keep the package ps_mem, I’ll undo the changes once again. This time from snapshot #2 to snapshot #1.
$ sudo snapper undochange 2..1
create:5 modify:2 delete:0
$ which ps_mem
/usr/bin/ps_mem
Test 2: Update the kernel and do a rollback.
In this rollback test, I'll update to the most latest kernel available at the time of writing. Then I'll boot into it and delete all of the kernel and initrd files, causing the system to fail the next time it boots. Then I'll check if I can recover the system by rolling back to the post snapshot of the kernel update and save the day.
Check the current kernel version.
$ uname -r
5.17.5-300.fc36.x86_64
Now update to the latest kernel.
$ sudo dnf -y update kernel
After the kernel update is finished, restart the system and check the kernel version.
$ uname -r
5.17.8-300.fc36.x86_64
Also, check the snapper for pre (#3) and post (#4) kernel update snapshots.
$ snapper ls
# | Type | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+----------------------------------+------+---------+-------------------------------+---------
0 | single | | | root | | current |
1 | pre | | Saturday 21 May 2022 11:47:50 AM | root | number | /usr/bin/dnf install ps_mem |
2 | post | 1 | Saturday 21 May 2022 11:47:55 AM | root | number | /usr/bin/dnf install ps_mem |
3 | pre | | Saturday 21 May 2022 12:28:21 PM | root | number | /usr/bin/dnf -y update kernel |
4 | post | 3 | Saturday 21 May 2022 12:29:26 PM | root | number | /usr/bin/dnf -y update kernel |
For the sake of the experiment, I’ll now delete all kernel and initrd files and make the system unusable.
$ sudo -i
# rm /boot/{vmlinuz,initramfs}*
rm: remove regular file '/boot/vmlinuz-0-rescue-aa65c3ac5d924efba3b0301f2a0cd167'? y
rm: remove regular file '/boot/vmlinuz-5.17.5-300.fc36.x86_64'? y
rm: remove regular file '/boot/vmlinuz-5.17.8-300.fc36.x86_64'? y
rm: remove regular file '/boot/initramfs-0-rescue-aa65c3ac5d924efba3b0301f2a0cd167.img'? y
rm: remove regular file '/boot/initramfs-5.17.5-300.fc36.x86_64.img'? y
rm: remove regular file '/boot/initramfs-5.17.8-300.fc36.x86_64.img'? y
When I rebooted the system, I got this boot error.
Now I’ll return to the GRUB menu and this time I’ll boot from snapshot #4 with the latest kernel.
Open the terminal and make sure everything is back to normal.
# uname -r
5.17.8-300.fc36.x86_64
# ll /boot/{vmlinuz,initramfs}*
-rw-------. 1 root root 103939016 May 19 22:41 /boot/initramfs-0-rescue-aa65c3ac5d924efba3b0301f2a0cd167.img
-rw-------. 1 root root 19805772 May 20 23:55 /boot/initramfs-5.17.5-300.fc36.x86_64.img
-rw-------. 1 root root 19806036 May 21 12:29 /boot/initramfs-5.17.8-300.fc36.x86_64.img
-rwxr-xr-x. 1 root root 11802352 May 20 21:11 /boot/vmlinuz-0-rescue-aa65c3ac5d924efba3b0301f2a0cd167
-rwxr-xr-x. 1 root root 11802352 Apr 28 21:51 /boot/vmlinuz-5.17.5-300.fc36.x86_64
-rwxr-xr-x. 1 root root 11803056 May 16 06:51 /boot/vmlinuz-5.17.8-300.fc36.x86_64
Because you booted into the snapshot, the ‘/‘ file system is read-only.
# btrfs property get -ts /
ro=true
Now that everything appears to be operating properly, it is time to roll back this snapshot in read-write mode.
# snapper ls
# | Type | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+----------------------------------+------+---------+-------------------------------+---------
0 | single | | | root | | current |
1 | pre | | Saturday 21 May 2022 11:47:50 AM | root | number | /usr/bin/dnf install ps_mem |
2 | post | 1 | Saturday 21 May 2022 11:47:55 AM | root | number | /usr/bin/dnf install ps_mem |
3 | pre | | Saturday 21 May 2022 12:28:21 PM | root | number | /usr/bin/dnf -y update kernel |
4- | post | 3 | Saturday 21 May 2022 12:29:26 PM | root | number | /usr/bin/dnf -y update kernel |
# snapper --ambit classic rollback 4
Ambit is classic.
Creating read-only snapshot of current system. (Snapshot 5.)
Creating read-write snapshot of snapshot 4. (Snapshot 6.)
Setting default subvolume to snapshot 6.
# reboot
After the reboot, check the system once again.
# uname -r
5.17.8-300.fc36.x86_64
# btrfs property get -ts /
ro=false
# snapper ls
# | Type | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+----------------------------------+------+---------+-------------------------------+--------------
0 | single | | | root | | current |
1 | pre | | Saturday 21 May 2022 11:47:50 AM | root | number | /usr/bin/dnf install ps_mem |
2 | post | 1 | Saturday 21 May 2022 11:47:55 AM | root | number | /usr/bin/dnf install ps_mem |
3 | pre | | Saturday 21 May 2022 12:28:21 PM | root | number | /usr/bin/dnf -y update kernel |
4 | post | 3 | Saturday 21 May 2022 12:29:26 PM | root | number | /usr/bin/dnf -y update kernel |
5 | single | | Saturday 21 May 2022 12:44:25 PM | root | number | rollback backup | important=yes
6* | single | | Saturday 21 May 2022 12:44:26 PM | root | | writable copy of #4 |
# btrfs subvolume get-default /
ID 268 gen 273 top level 261 path snapshots/6/snapshot
# btrfs subvolume list /
ID 256 gen 274 top level 5 path home
ID 257 gen 201 top level 5 path opt
ID 258 gen 274 top level 5 path tmp
ID 259 gen 274 top level 5 path var
ID 260 gen 205 top level 5 path usr-local
ID 261 gen 274 top level 5 path snapshots
ID 262 gen 271 top level 259 path var/lib/machines
ID 263 gen 245 top level 261 path snapshots/1/snapshot
ID 264 gen 247 top level 261 path snapshots/2/snapshot
ID 265 gen 251 top level 261 path snapshots/3/snapshot
ID 266 gen 267 top level 261 path snapshots/4/snapshot
ID 267 gen 266 top level 261 path snapshots/5/snapshot
ID 268 gen 273 top level 261 path snapshots/6/snapshot
# ll /boot/{vmlinuz,initramfs}*
-rw-------. 1 root root 103939016 May 19 22:41 /boot/initramfs-0-rescue-aa65c3ac5d924efba3b0301f2a0cd167.img
-rw-------. 1 root root 19805772 May 20 23:55 /boot/initramfs-5.17.5-300.fc36.x86_64.img
-rw-------. 1 root root 19806036 May 21 13:48 /boot/initramfs-5.17.8-300.fc36.x86_64.img
-rwxr-xr-x. 1 root root 11802352 May 20 21:11 /boot/vmlinuz-0-rescue-aa65c3ac5d924efba3b0301f2a0cd167
-rwxr-xr-x. 1 root root 11802352 Apr 28 21:51 /boot/vmlinuz-5.17.5-300.fc36.x86_64
-rwxr-xr-x. 1 root root 11803056 May 16 06:51 /boot/vmlinuz-5.17.8-300.fc36.x86_64
After a successful rollback, always ensure that the grub.cfg file is updated so that the snapshots on the grub-btrfs menu are always in sync.
# grub2-mkconfig -o /boot/grub2/grub.cfg
You have now successfully reverted to the most recent kernel fully functional with read-write mode.
Both tests were successful.
9. Conclusion
With Fedora 36 installed with LUKS Full Disk Encryption, snapper, and grub-btrfs, you can now be certain that your data at rest is safe, and if you make a mistake, you can easily roll back to a previous functioning snapshot.
You can also better secure your machine from Evil Maid attacks by using UEFI Secure Boot custom key enrollment and a self-signed kernel and bootloader. Check out my other blog 'Enable Fedora UEFI Secure Boot with Custom Keys and Self-Signed Bootloader, Kernel, and Modules' on how to do this.
If you want to configure the snapper to take snapshots of the home subvolume as well, or if you want to configure the snapper to take snapshots at predetermined intervals, see the Arch Wiki webpage for further information.