Install Fedora 41 with Snapshot and Rollback Support - Feature Image

How to Install Fedora 41 with Snapshot and Rollback Support

Fedora Linux 41 has been released!

In this guide, I will show you how to install Fedora 41 with snapshot and rollback support.

So, if you mess up the system settings, install something that does not work, or run into problems after updating, you won't have to worry about breaking the system and spending hours trying to fix it. Simply restore the snapshot before the problematic change, and you are good to go.

Note: If you want to install Fedora 41 with snapshot and rollback support while also having a LUKS Full Disk Encryption, please see my other guide 'How to Install Fedora 41 with Full Disk Encryption, Snapshot, and Rollback Support'.

Note: If you have previously installed Fedora 40 following my earlier tutorial and wish to upgrade to Fedora 41, please proceed directly to the upgrade section located at the end of this article to upgrade from Fedora 40 to Fedora 41. No additional steps are required.

So let's get started.

Table of Contents

1. Partitions and Subvolumes Layout

For this guide, I will use a hard disk with a capacity of 128 GiB.

I will create a 600 MiB EFI system partition and use the remaining disk space to create a partition for the Linux file system.

Fedora also creates a SwapOnZRAM during boot, so no separate swap partition is required.

This is how the disk partition looks:

NAME         SIZE  FSTYPE  PARTTYPENAME      MOUNTPOINT
/dev/vda 128G
/dev/vda1 600M vfat EFI System /boot/efi
/dev/vda2 127.4G btrfs Linux filesystem /

I will create a btrfs file system on the Linux partition (/dev/vda2). Then, on the btrfs file system, I will create the subvolumes listed below to keep them out of root file system snapshots.

NAME             MOUNTPOINT                 TYPE
[main] / mainvolume
home /home subvolume
opt /opt subvolume
cache /var/cache subvolume
crash /var/crash subvolume
AccountsService /var/lib/AccountsService subvolume
gdm /var/lib/gdm subvolume
images /var/lib/libvirt/images subvolume
log /var/log subvolume
spool /var/spool subvolume
tmp /var/tmp subvolume
www /var/www subvolume
.mozilla /home/$USER/.mozilla subvolume
.snapshots /.snapshots subvolume
.snapshots /home/.snapshots subvolume

Info: Subvolumes, unlike standard partitions or LVM logical volumes, do not have a size; rather, they behave as directories with shared space. However, the resemblance between subvolumes and directories ends here. Each subvolume, much like a file system, has its own file tree, POSIX namespace, and inode pool. This means that hard links between subvolumes are not possible. From this perspective, a subvolume resembles a separate file system. Subvolumes are not block devices either.

The directories for which subvolumes are created and the reasons for doing so are listed below. For more information, visit this website.

/home

Contains user data. It will be created to keep user data separate while also protecting against data loss during system root rollbacks.

/opt

Contains applications installed by a third party. It will be created to keep these applications from being uninstalled during rollbacks.

/var/cache, /var/crash, /var/tmp

Directories containing temporary files and caches. They will be created so that the system root subvolume does not contain temporary files and caches. Fedora mounts /tmp with tmpfs on boot, so a separate subvolume is not required.

/var/lib/AccountsService, /var/lib/gdm

Contains login user and Gnome display information. These directories must be writeable at all times. When you try to boot a snapshot from the GRUB menu to rollback system root, you are booting a read-only snapshot, and because it cannot be written to, the system hangs just before the Gnome login screen appears.

If you are using a desktop environment other than Gnome, you will need to replace '/var/lib/gdm' with one specific to your desktop environment.

For KDE, it is '/var/lib/sddm'.

For XFCE, it is '/var/lib/lightdm' and '/var/lib/lightdm-data'.

To find out about other desktop environments, refer to this page.

/var/lib/libvirt/images

The default location for libvirt-managed virtual machine images. It will be created so that virtual machine images are not replaced with older versions during a rollback.

/var/log

Contains log files. It will be created to prevent the loss of log data during rollbacks.

/var/spool

Contains data that is awaiting some kind of later processing, such as mail, mail queues, printing, printer spool, and so on. It will be created to prevent the loss of mail, printing, and spool data following a rollback.

/var/www

Web server directory. It will be created to keep hosted web server data separate and prevent data loss during system root rollbacks.

/home/$USER/.mozilla

Fedora Workstation ships with the Firefox web browser by default. If you take a snapshot of your home directory and then undo the changes, you do not want to lose any new bookmarks, passwords, or other user data.

So I will create a subvolume for the '.mozilla' directory, which is where Firefox stores all of its data. This separates it from the home subvolume, avoiding data loss during undo operations on the home subvolume.

If you use other web browsers, you should consider creating separate subvolumes for them.

Chrome Browser: /home/$USER/.config/google-chrome
Brave Browser : /home/$USER/.config/BraveSoftware

Depending on your usage, you may also want to consider creating subvolumes in your home directory for the following directories:

GnuPG: /home/$USER/.gnupg
SSH: /home/$USER/.ssh
Thunderbird Mail: /home/$USER/.thunderbird
/.snapshots, /home/.snapshots

Contains snapshots of the root (/) and home (/home) directories.

2. Install Fedora Workstation 41

Boot your system using the Fedora Workstation 41 installer in UEFI mode.

On the welcome screen, select the Install to Hard Drive option. Next, select your LanguageKeyboard, 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.

On the BLIVET GUI PARTITIONING screen, create the partitions, file systems, and btrfs subvolumes necessary to install the Fedora Workstation 41.

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 600 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 Workstation 41.

Select the free space again and click on the + sign to create a Btrfs volume. Set the Filesystem to btrfs and the Mountpoint to /. I used the entire remaining space. However, you can specify the size you want for the btrfs volume.

Next, you must create a home subvolume. (1) Select the Btrfs Volume from the left panel, and (2) click on the + sign on the right panel.

Create a home subvolume. Enter the Name as home and Mountpoint as /home. Click OK to finish.

I will create the remaining subvolumes when the installation is finished. This is because the Anaconda installer does not allow you to create subvolumes with slashes (/) in their names, such as var/log.

For now, click Done to finish creating subvolumes.

On the SUMMARY OF CHANGES screen, double-check that everything is properly defined. 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.

The last phase of the installation process will start. Click the Start Setup button to complete the remaining customization steps, such as setting a new login, password, etc.

You will then be logged into the Fedora Workstation 41 with the all-new Gnome 47 desktop interface.

When booting your system, you may have noticed the following error message.

This is because you did not create a separate ext4 /boot partition and instead included it in the btrfs system root. GRUB preboot writes to /boot/grub2/grubenv if the boot was successful. This error occurs because of the GRUB btrfs.mod driver, unlike ext4, is read-only.

To resolve this, open the Gnome terminal and execute the following command.

$ sudo grub2-editenv - unset menu_auto_hide

Set the btrfs volume label. I will name the btrfs volume FEDORA, but you can name it whatever you want.

$ sudo btrfs filesystem label / FEDORA

$ sudo btrfs filesystem show /
Label: 'FEDORA'  uuid: 22200c17-4498-4c16-b012-54a56fe5ba8d
	Total devices 1 FS bytes used 6.93GiB
	devid    1 size 127.41GiB used 10.02GiB path /dev/vda2

Your setup should look something like this.

$ lsblk -p /dev/vda
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
/dev/vda    252:0    0   128G  0 disk 
├─/dev/vda1 252:1    0   600M  0 part /boot/efi
└─/dev/vda2 252:2    0 127.4G  0 part /home
                                      /

And the subvolumes will look like this.

$ sudo btrfs subvolume list /
ID 256 gen 44 top level 5 path home
ID 257 gen 31 top level 5 path var/lib/machines

In Fedora Workstation, systemd automatically creates the /var/lib/machines subvolume for systemd-nspawn containers.

Install the packages listed below.

$ sudo dnf install vim git inotify-tools make

Now, update your operating system.

$ sudo dnf update

And reboot your system.

$ sudo reboot

3. Create the Additional Subvolumes

I will now proceed to create the remaining subvolumes on the system root.

These subvolumes are created on the system root to avoid being included in the rollback process. Some of these directories store temporary files and caches. Some contain data that you do not want to lose if you roll back the system root. And some should be in read-write mode when booting into a read-only snapshot.

Create the directory '/var/lib/libvirt'. Only applicable to other Fedora Spins; not required for Fedora Workstation.

$ sudo mkdir -vp /var/lib/libvirt

Get the UUID of your btrfs root file system.

$ ROOT_UUID="$(sudo grub2-probe --target=fs_uuid /)" ; echo $ROOT_UUID
22200c17-4498-4c16-b012-54a56fe5ba8d

Get the btrfs subvolume mount options from the /etc/fstab file.

$ OPTIONS="$(grep '/home' /etc/fstab \
    | awk '{print $4}' \
    | cut -d, -f2-)" \
    ; echo $OPTIONS
compress=zstd:1

Declare the remaining subvolumes you want to create in the 'SUBVOLUMES' bash array. Copy from 'SUBVOLUMES' to ')', paste into the terminal, and press [ENTER].

$ SUBVOLUMES=(
    "opt"
    "var/cache"
    "var/crash"
    "var/lib/AccountsService"
    "var/lib/gdm"
    "var/lib/libvirt/images"
    "var/log"
    "var/spool"
    "var/tmp"
    "var/www"
    "home/$USER/.mozilla"
)

If you are installing Fedora for KDE Desktop, replace "var/lib/gdm" with:

"var/lib/sddm"

If you are installing Fedora for XFCE Desktop, replace "var/lib/gdm" with:

"var/lib/lightdm"
"var/lib/lightdm-data"

If you intend to install Google Chrome web browser, you should also create a subvolume for it. Add the following to the SUBVOLMES array:

"home/$USER/.config/google-chrome"

If you intend to install Brave web browser, you should also create a subvolume for it. Add the following to the SUBVOLMES array:

"home/$USER/.config/BraveSoftware"

If you intend to install the Mozilla Thunderbird email client, add the following to the SUBVOLMES array:

"home/$USER/.thunderbird"

If you use the GnuPG encryption and signing tool, you must create a subvolume to avoid losing your keys.

"home/$USER/.gnupg"

Similarly, if you use the SSH remote login client, you must create a subvolume to prevent losing your keys.

"home/$USER/.ssh"

For this tutorial, however, I will only create the '.mozilla' subvolume in the user's home directory.

$ printf '%s\n' "${SUBVOLUMES[@]}"
opt
var/cache
var/crash
var/lib/AccountsService
var/lib/gdm
var/lib/libvirt/images
var/log
var/spool
var/tmp
var/www
home/madhu/.mozilla

Find the length of the longest element in the SUBVOLMES array.

$ MAX_LEN="$(printf '/%s\n' "${SUBVOLUMES[@]}" | wc -L)" ; echo $MAX_LEN
24

Now, create subvolumes and also add them to the /etc/fstab file. Copy from 'for' to 'done', paste it in the terminal, and hit [ENTER].

$ for dir in "${SUBVOLUMES[@]}" ; do
    if [[ -d "/${dir}" ]] ; then
        sudo mv -v "/${dir}" "/${dir}-old"
        sudo btrfs subvolume create "/${dir}"
        sudo cp -ar "/${dir}-old/." "/${dir}/"
    else
        sudo btrfs subvolume create "/${dir}"
    fi
    sudo restorecon -RF "/${dir}"
    printf "%-41s %-${MAX_LEN}s %-5s %-s %-s\n" \
        "UUID=${ROOT_UUID}" \
        "/${dir}" \
        "btrfs" \
        "subvol=${dir},${OPTIONS}" \
        "0 0" | \
        sudo tee -a /etc/fstab
done

Once you have finished creating subvolumes based on your requirements, change the ownership of the newly created directories in the user's home to your username.

$ sudo chown -cR $USER:$USER ~/$(ls -A)
$ sudo restorecon -vRF ~/$(ls -A)

If you have created subvolumes '.gnupg' and '.ssh', you must change their permissions.

$ sudo chmod -vR 0700 ~/{.gnupg,.ssh}

Examine your /etc/fstab file. This is what it should look like. The UUID and username will be unique to your system.

$ cat /etc/fstab
....
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /                        btrfs defaults        0 0
UUID=845B-87C2                            /boot/efi                vfat  umask=0077,shortname=winnt 0 2
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /home                    btrfs subvol=home,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /opt                     btrfs subvol=opt,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /var/cache               btrfs subvol=var/cache,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /var/crash               btrfs subvol=var/crash,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /var/lib/AccountsService btrfs subvol=var/lib/AccountsService,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /var/lib/gdm             btrfs subvol=var/lib/gdm,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /var/lib/libvirt/images  btrfs subvol=var/lib/libvirt/images,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /var/log                 btrfs subvol=var/log,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /var/spool               btrfs subvol=var/spool,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /var/tmp                 btrfs subvol=var/tmp,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /var/www                 btrfs subvol=var/www,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /home/madhu/.mozilla     btrfs subvol=home/madhu/.mozilla,compress=zstd:1 0 0

Reload /etc/fstab to mount all the subvolumes.

$ sudo systemctl daemon-reload

$ sudo mount -va
/                        : ignored
/boot/efi                : already mounted
/home                    : already mounted
/opt                     : successfully mounted
/var/cache               : successfully mounted
/var/crash               : successfully mounted
/var/lib/AccountsService : successfully mounted
/var/lib/gdm             : successfully mounted
/var/lib/libvirt/images  : successfully mounted
/var/log                 : successfully mounted
/var/spool               : successfully mounted
/var/tmp                 : successfully mounted
/var/www                 : successfully mounted
/home/madhu/.mozilla     : successfully mounted

Check your subvolumes.

$ sudo btrfs subvolume list /
ID 256 gen 80 top level 5 path home
ID 257 gen 70 top level 5 path var/lib/machines
ID 258 gen 79 top level 5 path opt
ID 259 gen 79 top level 5 path var/cache
ID 260 gen 79 top level 5 path var/crash
ID 261 gen 79 top level 5 path var/lib/AccountsService
ID 262 gen 79 top level 5 path var/lib/gdm
ID 263 gen 79 top level 5 path var/lib/libvirt/images
ID 264 gen 79 top level 5 path var/log
ID 265 gen 79 top level 5 path var/spool
ID 266 gen 79 top level 5 path var/tmp
ID 267 gen 79 top level 5 path var/www
ID 268 gen 79 top level 256 path home/madhu/.mozilla

Your setup should now look somewhat like this.

$ lsblk -p /dev/vda
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
/dev/vda    252:0    0   128G  0 disk 
├─/dev/vda1 252:1    0   600M  0 part /boot/efi
└─/dev/vda2 252:2    0 127.4G  0 part /home/madhu/.mozilla
                                      /var/www
                                      /var/tmp
                                      /var/spool
                                      /var/log
                                      /var/lib/libvirt/images
                                      /var/lib/gdm
                                      /var/lib/AccountsService
                                      /var/crash
                                      /var/cache
                                      /opt
                                      /home
                                      /

Now that everything appears to be in order, you can delete the old directories.

$ for dir in "${SUBVOLUMES[@]}" ; do
    if [[ -d "/${dir}-old" ]] ; then
        sudo rm -rvf "/${dir}-old"
    fi
done

4. Install and Configure Snapper

Fedora 41 now uses dnf5 as its default package manager, replacing dnf4.

Unlike dnf4, the new dnf5 is a fully featured package manager that does not require Python dependencies.

As a result, Fedora 41 no longer includes the snapper plugin python3-dnf-plugin-snapper. This plugin was in charge of creating pre and post snapshosts whenever packages were installed using the dnf4 package manager.

In dnf5, a new way to implement this function is introduced: the 'Actions plugin'. The Actions plugin is more powerful than the previous snapper plugin, as it can run external applications before or after transactions and interact with the dnf5 configuration.

So let us install the necessary packages.

$ sudo dnf install snapper libdnf5-plugin-actions

Create an Actions file called snapper.actions. Copy from 'sudo' to 'EOF', paste it into the terminal, and hit [Enter]. (Sources: 1 and 2)

$ sudo bash -c "cat > /etc/dnf/libdnf5-plugins/actions.d/snapper.actions" <<'EOF'
# Get snapshot description
pre_transaction::::/usr/bin/sh -c echo\ "tmp.cmd=$(ps\ -o\ command\ --no-headers\ -p\ '${pid}')"

# Creates pre snapshot before the transaction and stores the snapshot number in the "tmp.snapper_pre_number"  variable.
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre_number=$(snapper\ create\ -t\ pre\ -c\ number\ -p\ -d\ '${tmp.cmd}')"

# If the variable "tmp.snapper_pre_number" exists, it creates post snapshot after the transaction and removes the variable "tmp.snapper_pre_number".
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre_number}"\ ]\ &&\ snapper\ create\ -t\ post\ --pre-number\ "${tmp.snapper_pre_number}"\ -c\ number\ -d\ "${tmp.cmd}"\ ;\ echo\ tmp.snapper_pre_number\ ;\ echo\ tmp.cmd
EOF

Create new snapper configurations named root and home for the btrfs volume at / and /home, respectively.

$ sudo snapper -c root create-config /
$ sudo snapper -c home create-config /home

The snapper configuration files will be saved in the /etc/snapper/configs/ directory.

Verify the snapper configuration files have been created.

$ sudo snapper list-configs
Config │ Subvolume
───────┼──────────
home   │ /home
root   │ /

Allow regular user to use snapper without requiring root privileges. Add your user name to the snapper's root and home configurations to set the ACL on the /.snapshots and /home/.snapshots directories.

$ sudo snapper -c root set-config ALLOW_USERS=$USER SYNC_ACL=yes
$ sudo snapper -c home set-config ALLOW_USERS=$USER SYNC_ACL=yes

Add the newly created snapshot subvolumes to the /etc/fstab file.

Execute the following commands sequentially. For the last command, copy from 'for' to 'done', paste it into your terminal, and press [ENTER].

$ ROOT_UUID="$(sudo grub2-probe --target=fs_uuid /)"

$ MAX_LEN="$(cat /etc/fstab | awk '{print $2}' | wc -L)"

$ OPTIONS="$(grep '/opt' /etc/fstab \
    | awk '{print $4}' \
    | cut -d, -f2-)"
    
$ for dir in '.snapshots' 'home/.snapshots' ; do
    printf "%-41s %-${MAX_LEN}s %-5s %-s %-s\n" \
        "UUID=${ROOT_UUID}" \
        "/${dir}" \
        "btrfs" \
        "subvol=${dir},${OPTIONS}" \
        "0 0" | \
        sudo tee -a /etc/fstab
done

Your /etc/fstab file should look like this. The UUIDs will be unique to your system.

$ cat /etc/fstab
....
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /.snapshots              btrfs subvol=.snapshots,compress=zstd:1 0 0
UUID=22200c17-4498-4c16-b012-54a56fe5ba8d /home/.snapshots         btrfs subvol=home/.snapshots,compress=zstd:1 0 0

Reload the /etc/fstab file.

$ sudo systemctl daemon-reload
$ sudo mount -va

Your subvolumes will look like this:

$ sudo btrfs subvolume list /
....
ID 269 gen 101 top level 5 path .snapshots
ID 270 gen 101 top level 256 path home/.snapshots

Disable the indexing of the .snapshots directories by updatedb. It is enabled by default, which can cause significant slowdown and excessive memory usage if you have a large number of snapshots.

$ echo 'PRUNENAMES = ".snapshots"' | sudo tee -a /etc/updatedb.conf

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

You must also make changes to the /boot/efi/EFI/fedora/grub.cfg file now that snapshot booting is enabled.

$ sudo sed -i.bkp1 '1i set btrfs_relative_path="yes"' /boot/efi/EFI/fedora/grub.cfg

Finally, update the grub.cfg file.

$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg

Later, I'll enable snapper timeline snapshots. For now, the snapper configuration is complete.

List the snapshots for / volume. For the root, you may use snapper -c root ls or simply snapper ls. Both provide the same output.

$ snapper ls
# │ Type   │ Pre # │ Date │ User │ Cleanup │ Description │ Userdata
──┼────────┼───────┼──────┼──────┼─────────┼─────────────┼─────────
0 │ single │       │      │ root │         │ current     │

List the snapshots for /home subvolume.

$ snapper -c home 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.

5. Install and Configure Grub-Btrfs

The grub-btrfs package adds 'Fedora Linux snapshots' to the GRUB menu and allows you to test a snapshot in read-only mode before rolling back to it in read-write mode.

Clone the grub-btrfs repository.

$ git clone https://github.com/Antynea/grub-btrfs

The setup is pre-configured to work with Arch and Gentoo out of the box. You must make a few changes to the grub-btrfs config file to make it work with Fedora.

Change the directory to grub-btrfs.

$ cd grub-btrfs

Copy from 'sed' to 'config', paste it into the terminal, and press [ENTER].

$ sed -i.bkp \
-e '/#GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS/a \
GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS="systemd.volatile=state"' \
-e '/#GRUB_BTRFS_GRUB_DIRNAME/a \
GRUB_BTRFS_GRUB_DIRNAME="/boot/grub2"' \
-e '/#GRUB_BTRFS_MKCONFIG=/a \
GRUB_BTRFS_MKCONFIG=/usr/sbin/grub2-mkconfig' \
-e '/#GRUB_BTRFS_SCRIPT_CHECK=/a \
GRUB_BTRFS_SCRIPT_CHECK=grub2-script-check' \
config

Then install it.

$ sudo make install

Update grub.cfg and enable the grub-btrfsd.service.

$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg
$ sudo systemctl enable --now grub-btrfsd.service

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.

Your grub-btrfs installation is now complete. You may now delete the cloned grub-btrfs repository.

$ cd ..
$ rm -rvf grub-btrfs

6. Create a System Root Snapshot and Set It as the Default

Now that everything in Fedora has been installed and configured, it is time to take a subvolume snapshot of the main volume and set the subvolume as the default ‘/‘ file system. By manually creating a subvolume snapshot of the main volume and making it the default, you can easily rollback without having to specify the snapshot number and the 'ambit' during the rollback when using the snapper tool.

Create a directory named '1' in the /.snapshots directory.

$ sudo mkdir -v /.snapshots/1

Create an XML file called info.xml in the /.snapshots/1/ directory. Copy the following command from 'sudo' to 'EOF', paste it into your terminal, and hit the [ENTER] button.

$ sudo bash -c "cat > /.snapshots/1/info.xml" <<EOF
<?xml version="1.0"?>
<snapshot>
  <type>single</type>
  <num>1</num>
  <date>$(date -u +"%F %T")</date>
  <description>first root subvolume</description>
</snapshot>
EOF

Verify that the date is set properly and is in the Coordinated Universal Time (UTC) format.

$ cat /.snapshots/1/info.xml
<?xml version="1.0"?>
<snapshot>
  <type>single</type>
  <num>1</num>
  <date>2024-10-28 19:22:11</date>
  <description>first root subvolume</description>
</snapshot>

Now, create a read-write subvolume snapshot of the system root in the /.snapshots/1/ directory.

$ sudo btrfs subvolume snapshot / /.snapshots/1/snapshot

Get the subvolid of the /.snapshots/1/snapshot subvolume.

$ SNAP_1_ID="$(sudo btrfs inspect-internal rootid /.snapshots/1/snapshot)"

$ echo ${SNAP_1_ID}
271

Using this subvolume ID, set the /.snapshots/1/snapshot subvolume as the default subvolume for the root (/) filesystem.

$ sudo btrfs subvolume set-default ${SNAP_1_ID} /

Confirm that the /.snapshots/1/snapshot subvolume has been set as the default for the / filesystem.

$ sudo btrfs subvolume get-default /
ID 271 gen 126 top level 269 path .snapshots/1/snapshot

Finally, reboot your system.

$ sudo reboot

After rebooting, check the snapshots in snapper.

$ snapper ls
 # │ Type   │ Pre # │ Date                            │ User │ Cleanup │ Description          │ Userdata
───┼────────┼───────┼─────────────────────────────────┼──────┼─────────┼──────────────────────┼─────────
0  │ single │       │                                 │ root │         │ current              │
1* │ single │       │ Mon 28 Oct 2024 03:22:11 PM EDT │ root │         │ first root subvolume │

As you can see, the /.snapshots/1/snapshot subvolume is also visible as snapshot #1 in snapper. The asterisk (*) indicates that this snapshot is the default and is currently active.

The installation of Fedora 41 with snapshot and rollback support is now complete. You can now begin taking snapshots.

I ran some tests to determine the capabilities of the snapper tool as well as to demonstrate how to use it. If you're trying this out in a virtual machine, I highly recommend running these tests and any other scenarios you think of where you might want to explore different ways to destroy and recover.

The link is here: Snapper Tests. Although these tests are conducted on Fedora 40, they also apply to Fedora 41. There are no changes in Fedora 41.

7. Enable Automatic Timeline Snapshots

Now that you've finished everything, you can enable automatic timeline snapshots. When the timeline is enabled, a snapshot is created once every hour. Once per day, the timeline cleanup algorithm cleans up the snapshots.

Automatic Timeline Snapshots are enabled by default in both root and home configurations. Enable it only for the system root and disable it for the home. See the Arch Wiki page on 'Automatic timeline snapshots' for more information.

$ sudo snapper -c home set-config TIMELINE_CREATE=no
$ sudo systemctl enable --now snapper-timeline.timer
$ sudo systemctl enable --now snapper-cleanup.timer

That's all. Every hour from now on, a 'single' snapshot will be created and cleaned up every other day.

$ snapper ls
 # │ Type   │ Pre # │ Date                            │ User │ Cleanup  │ Description          │ Userdata
───┼────────┼───────┼─────────────────────────────────┼──────┼──────────┼──────────────────────┼─────────
0  │ single │       │                                 │ root │          │ current              │
1* │ single │       │ Mon 28 Oct 2024 03:22:11 PM EDT │ root │          │ first root subvolume │
2  │ single │       │ Mon 28 Oct 2024 04:00:11 PM EDT │ root │ timeline │ timeline             │
3  │ single │       │ Mon 28 Oct 2024 05:00:11 PM EDT │ root │ timeline │ timeline             │

If you want to stop timeline snapshots at any time, simply disable the snapper timers.

$ sudo systemctl disable --now snapper-timeline.timer
$ sudo systemctl disable --now snapper-cleanup.timer

The installation of Fedora 41 with snapshot and rollback support is now complete.

8. Issues and Possible Solutions

Issue: This usually occurs when a kernel update has just been released and all of the kernel's supporting files have not yet been propagated to a repository mirror near you.

This is caused by the kernel parameter '{extra_cmdline}'. The '{extra_cmdline}' parameter is added to the kernel when you add the 'SUSE_BTRFS_SNAPSHOT_BOOTING=true' key to the /etc/default/grub file.

Solution: From the GRUB menu, select the kernel version you want to boot, and then press 'e' to modify the command.

Go to the line that starts with the word 'linux', and move the parameter '{extra_cmdline}' to the end of the line. The parameter '{extra_cmdline}' should always be at the end of the line.

From this:

Install Fedora 41 with Snapshot and Rollback Support - Grub Error 1

To this:

Install Fedora 41 with Snapshot and Rollback Support - Grub Error 2

Then, on your keyboard, press Ctrl+x. You will be successfully booted into the operating system. Next, launch the terminal and execute the following command.

$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg

Reboot the computer to ensure the problem has been resolved.

Issue: The problem appears to be that GRUB does not support TPM 2.0 very well when enabled in UEFI System Settings.

Solution: I looked into it, and it appears that this solution works perfectly. All credit goes to Eric Renfro.

Create a script named 02_tpm in the /etc/grub.d/ directory with the following contents.

# vim /etc/grub.d/02_tpm
#!/usr/bin/sh -e
echo "rmmod tpm"

Set the execute permission on a file.

# chmod +x /etc/grub.d/02_tpm

Update the grub.cfg file.

# grub2-mkconfig -o /boot/grub2/grub.cfg

This issue should now be resolved.

9. Upgrade Fedora 40 to Fedora 41

If you have already installed Fedora 40 using an earlier version of this tutorial and want to upgrade to Fedora 41, this section is for you.

First, if you are using timeline snapshots, temporarily disable them. Once the upgrade is complete, you can re-enable them.

$ sudo systemctl disable --now snapper-timeline.timer
$ sudo systemctl disable --now snapper-cleanup.timer

Next, take a snapshot of the system root in its current state. So, if something goes wrong, you can always roll back and start over.

$ snapper create -d 'Fedora 40 restore point'

Update your Fedora 40 release. This is important. Do not skip this step.

$ sudo dnf upgrade --refresh -y

After the updates have been installed, reboot your computer.

$ sudo reboot

After the reboot, if you encounter the 'lexer.c:352:syntax' error, refer to Section 8 Problem 1 to boot normally.


After rebooting, open the terminal and run the following command to upgrade your Fedora 40 operating system to the most recent Fedora 41 version.

$ sudo dnf system-upgrade download --releasever=41 -y

Once the upgrades have been downloaded, trigger the upgrade process. This will immediately reboot your computer, with no countdown or confirmation. So, before issuing the following command, close all other programs and save your work.

$ sudo dnf system-upgrade reboot

Once the upgrade process is completed, your system will reboot again into the updated version of Fedora 41.

Open the terminal and execute the following command to verify that Fedora has been updated to version 41.

$ cat /etc/fedora-release
Fedora release 41 (Forty One)

Remove the package python3-dnf-plugin-snapper, as it is no longer supported in Fedora 41.

$ sudo dnf remove python3-dnf-plugin-snapper -y

And install the Libdnf5 plugin, which allows you to run actions.

$ sudo dnf install libdnf5-plugin-actions -y

Create an Actions file called snapper.actions. Copy from 'sudo' to 'EOF', paste it into the terminal, and hit [Enter]. (Sources: 1 and 2)

$ sudo bash -c "cat > /etc/dnf/libdnf5-plugins/actions.d/snapper.actions" <<'EOF'
# Get snapshot description
pre_transaction::::/usr/bin/sh -c echo\ "tmp.cmd=$(ps\ -o\ command\ --no-headers\ -p\ '${pid}')"

# Creates pre snapshot before the transaction and stores the snapshot number in the "tmp.snapper_pre_number"  variable.
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre_number=$(snapper\ create\ -t\ pre\ -c\ number\ -p\ -d\ '${tmp.cmd}')"

# If the variable "tmp.snapper_pre_number" exists, it creates post snapshot after the transaction and removes the variable "tmp.snapper_pre_number".
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre_number}"\ ]\ &&\ snapper\ create\ -t\ post\ --pre-number\ "${tmp.snapper_pre_number}"\ -c\ number\ -d\ "${tmp.cmd}"\ ;\ echo\ tmp.snapper_pre_number\ ;\ echo\ tmp.cmd
EOF

The snapper configuration is now complete.

One final step before starting to use Fedora 41 is to set the relative path back in the grub.cfg file in the ESP.

When you upgrade to Fedora 41, the snapshot number from which you upgraded will be used as the new absolute path.

So, switch to super user mode and examine the grub.cfg file.

$ sudo -i

# cat /boot/efi/EFI/fedora/grub.cfg
search --no-floppy --root-dev-only --fs-uuid --set=dev e2b0f5a1-c2d5-4b62-8f6b-23861449b62e
set prefix=($dev)/.snapshots/1/snapshot/boot/grub2
export $prefix
configfile $prefix/grub.cfg

The output above shows that the snapshot path in the prefix has been changed to an absolute path, which is highlighted in amber.

In my case, it is snapshot number 1, from which I upgraded to Fedora 41. In your case, the snapshot number could be different.

The issue is that if you roll back, regardless of the snapshot number you roll back from, you always end up rolling back to snapshot number 1 (in my case).

To resolve this, execute the commands listed below in order.

# DEF_SNAP="$(btrfs subvolume get-default / \
    | awk '{print $NF}')/" \
    ; echo $DEF_SNAP
.snapshots/1/snapshot/

# sed -i.bkp2 \
    -e '1i set btrfs_relative_path="yes"' \
    -e "s|$DEF_SNAP||g" \
    /boot/efi/EFI/fedora/grub.cfg

Now, check the grub.cfg file once more. The end result should look something like this.

# cat /boot/efi/EFI/fedora/grub.cfg
set btrfs_relative_path="yes"
search --no-floppy --root-dev-only --fs-uuid --set=dev e2b0f5a1-c2d5-4b62-8f6b-23861449b62e
set prefix=($dev)/boot/grub2
export $prefix
configfile $prefix/grub.cfg

Update the grub.cfg file.

# grub2-mkconfig -o /boot/grub2/grub.cfg

You can now re-enable timeline snapshots if you previously disabled them.

# systemctl enable --now snapper-timeline.timer
# systemctl enable --now snapper-cleanup.timer

That is it. Fedora has been successfully upgraded to version 41. Reboot your system and you are good to go.

10. Watch on YouTube


Subscribe
Notify of
guest
21 Comments
Newest
Oldest
Inline Feedbacks
View all comments