Whether you've been introduced to Fedora by watching recent YouTube videos on it, have known about it for some time and it's finally time to give it a real try, or you've used it in the past and are eager to get back to all that it offers, this guide might be helpful to you if you're looking to set up BTRFS snapshots right away.
Fedora is a fantastic choice with a lot going on for itself. It's out of the box experience is wonderful, with most everything just working. This is a really nice choice for people who want their machine to work and don't want to spend potentially large amounts of time tinkering and bug fixing.
With that said, Fedora's out of the box BTRFS configuration unfortunately leaves a bit to be desired. This is what this guide sets out to solve.
If you're less familiar with what snapshots are or how they can be used; they basically allow your system to make a moment-in-time capture of your drive or directory, allowing you to revert back to that state at a later point if needed. It does this without making a copy of everything, so unlike more traditional backup or clone type solutions, this won't actually fill up your drive super quick or take ages to complete.
BTRFS works by what it calls Copy-on-Write (delightfully abbreviated to CoW. Moo!), which means whenever writes occur it doesn't actually overwrite existing data, it instead creates a new modified copy of the data block elsewhere, and updates the metadata to point to that block instead. This is what BTRFS' snapshot functionality uses to allow for these moments in time to be captured very quickly and, at least initially, take up basically no additional space.
What we're going to walk through will be compatible with all more recent versions of Fedora. I have tested and used this with Fedora 34, 35, as-well as Fedora 36 – which is in beta at the time of this writing.
This guide assumes you are planning on installing Fedora as the only OS to a drive. If you are planning on dual booting with another OS, please ensure you adjust the necessary steps to accommodate this. It might be useful to follow a guide on that specific subject if needed.
With a Fedora live USB stick created, boot up your system and select the install Fedora option when prompted. After selecting your region/language, you'll be greeted with the following screen.
This might be one of the least intuitive parts of the Fedora installation, but they're apparently working on an updated installer that should remedy this. Regardless, unless you are planning on dual booting, the steps here are fortunately very straight-forward – UX oddities aside.
Installation Destination option under the System header, in the following screen you'll be able to select which drive you'd like to install Fedora too. If you only have one drive, you might find that it is already selected. You can tell by the white-on-black check mark that appears on the drive.
By default the installer will have also selected the "Automatic" storage configuration option. Unless you need to customize things for a dual-boot setup or so, this is the easiest route to take.
In case your drive already has another OS installed, you should tick the "I would like to make additional space available" checkbox. This will let you remove existing partitions, after which the Fedora installer's "automatic" mode can create the necessary partitions.
Once you're ready, click the "Done" button hidden away in the top-right corner of the installer, and then proceed with the installation by clicking the start installation button.
This will take a little bit, after which your system should automatically reboot and bring you to the account setup, and final, step of the Fedora installation process. Follow these final steps until you are greeted with Fedora's nice and clean desktop.
With the installation done and your system booted into a nice and fresh Fedora installation, let's actually make the necessary changes to get our root-level snapshots working the way we want it. We'll also install and configure a tool that automatically creates snapshots before and after
dnf upgrades, too.
In case you haven't already, it might be a good idea to update your system right away so everything is fully up-to-date.
For these next few steps we'll mostly be using terminal commands, so let's open up Terminal now.
By default Fedora's installation configures two BTRFS subvolumes; one for your root drive (
/), the other for your home directory (
/home). You can confirm this by running the following command:
❯ sudo btrfs subvolume list / | grep "level 5"
You'll see something like this as the result:
❯ sudo btrfs subvolume list / | grep "level 5" ID 256 gen 36 top level 5 path home ID 257 gen 36 top level 5 path root
Let's proceed. We'll install a utility called
snapper, along with its
dnf plugin. Run the following command to install both:
❯ sudo dnf install snapper python-dnf-plugin-snapper
snapper installed, let's create and configure snapshots for the root partition first. Run teh following command to do this:
❯ sudo snapper -c root create-config /
Creating a root-level
Creating this configuration also creates a snapshots subvolume. However, this is created as as subvolume directly under the root partition, which we don't want. You can see what I mean by listing out the btrfs subvolumes again. You'll see something like this:
❯ sudo btrfs subvolume list / ID 256 gen 43 top level 5 path home ID 257 gen 58 top level 5 path root ID 258 gen 25 top level 257 path var/lib/machines ID 259 gen 58 top level 257 path .snapshots
.snapshots volume with its level of
257. To correct this, let's delete this automatically created subvolume and create a new one that's at the same level as the root and home subvolumes. First, let's delete the subvolume:
❯ sudo btrfs subvolume delete /.snapshots
Now we can re-create it, but at the right level. As you noticed earlier, the default Fedora installation has two subvolumes. Even though we look at
/ as the root volume, in this case it actually exists in a subvolume of its own. The actual root of your drive isn't mounted directly, only the two subvolumes are.
In order for us to create another subvolume at this higher level, we need to temporarily mount the real root drive so we can run the appropriate commands from there.
There's several ways to do this. For this guide we'll use the drive's UUID. This is easily discoverable by listing out the contents of your system's
fstab file, like so:
❯ cat /etc/fstab
The results will look something like this:
UUID=2271c46d-9093-4373-9b9f-4f4bac3f944f / btrfs subvol=root,compress=zstd:1 0 0 UUID=a83793bc-31dc-4d79-b4b9-adadafdde13b /boot ext4 defaults 1 2 UUID=2271c46d-9093-4373-9b9f-4f4bac3f944f /home btrfs subvol=home,compress=zstd:1 0 0
In my example –which is running inside a virtual machine– the main drive's UUID is
2271c46d-9093-4373-9b9f-4f4bac3f944f, you can see how both the
/home subvolumes reference this same UUID. Take a look at your own system and make note of the primary drive's UUID. We'll need it for the next step.
Let's create a new empty directory where the snapshots will be mounted, as-well as a temporary directory to which we can mount the drive, and then mount the actual drive to it:
❯ sudo mkdir /mnt/btrfs /.snapshots ❯ sudo mount /dev/disk/by-uuid/2271c46d-9093-4373-9b9f-4f4bac3f944f /mnt/btrfs
Substitute the UUID with the one you found.
Now with the actual drive mounted, let's cd into it and create the new root-level snapshot subvolume:
❯ cd /mnt/btrfs ❯ sudo btrfs subvolume create snapshots
Let's confirm that everything looks alright by listing out all subvolumes. The results should look something like this:
❯ sudo btrfs subvolume list / | grep "level 5" ID 256 gen 129 top level 5 path home ID 257 gen 132 top level 5 path root ID 259 gen 132 top level 5 path snapshots
With this done, we can unmount the root drive again, clean up after ourselves, and continue with the final bits of configuration.
❯ cd ~ ❯ sudo umount /mnt/btrfs ❯ sudo rmdir /mnt/btrfs
Using your favorite text editor with
sudo permissions, open up
/etc/fstab and let's add the new sub-volume. We do this by adding a new line to this file that references the same UUID we used just before, and references the newly created
The easiest way is to just duplicate the line already in your
fstab file for the
home sub-volume and changing the mount path as-well as
subvol value. The end result should look something like this:
UUID=2271c46d-9093-4373-9b9f-4f4bac3f944f / btrfs subvol=root,compress=zstd:1 0 0 UUID=a83793bc-31dc-4d79-b4b9-adadafdde13b /boot ext4 defaults 1 2 UUID=2271c46d-9093-4373-9b9f-4f4bac3f944f /home btrfs subvol=home,compress=zstd:1 0 0 UUID=2271c46d-9093-4373-9b9f-4f4bac3f944f /.snapshots btrfs subvol=snapshots,compress=zstd:1 0 0
Save and close the file. We can now try to auto mount everything to make sure everything is working as it should by running:
❯ sudo systemctl daemon-reload ❯ sudo mount -a
For example, if you use Docker, creating a sub-volume and mounting it to
/var/lib/dockerwould prevent root snapshots from filling up with docker volume data and also ensure that if you do roll back your root to a previous state, you don't lose anything related to your docker containers when doing so.
If you'd like to do this, you can effectively follow the same steps listed above, but instead create a sub-volume called something like
docker, and adding a row in your
fstabfile that mounts it to the path mentioned above. If you already have existing running containers and volumes there, you can simply temporarily disable Docker, temporarily move all contents of the
btrfsfolder elsewhere, and move everything back after creating this sub-volume.
Another example might be if you install Steam games on your root drive. You could create a sub-volume specifically for the
~/.local/share/Steamdirectory, ensuring that reverting to a previous snapshot of your home directory won't make you lose your already downloaded games too.
By default Fedora configures grub to simply reference the top level as the default subvolume. We need to change this to be able to support root subvolume rollbacks. First, let's check what the current configuration says:
❯ sudo btrfs subvolume get-default / ID 5 (FS_TREE)
Recall when listing out the BTRFS subvolumes that we could see their respective IDs:
❯ sudo btrfs subvolume list / | grep "level 5" ID 256 gen 129 top level 5 path home ID 257 gen 132 top level 5 path root ID 259 gen 132 top level 5 path snapshots
In my example's case, the
root subvolume has an ID of
257. Check your system's IDs and once you've found the correct one for your
root subvolume, update the default value with the following command:
❯ sudo btrfs subvolume set-default 257 /
Now when checking again, it should look something like this:
❯ sudo btrfs subvolume get-default / ID 257 gen 143 top level 5 path root
Next we need to update the Grub configuration to not specifically reference the
root subvolume by name. Fedora comes with a utility called
grubby by default which seems to be the Fedora way of doing this. We want to remove this reference by name so that the
default value we have just configured can do its thing:
❯ sudo grubby --update-kernel=ALL --remove-args="rootflags=subvol=root"
With that done, we should now be ready to enjoy root-level snapshots with the ability to rollback. Let's reboot now.
Now every time you install, update, or remove something through
dnf, snapshots are automatically created before and after these actions. This includes anything you might install/update/remove through the Software Center GUI application – though not when installing flatpaks of course.
Here's an example of what my
snapper ls results look like after installing 0 A.D.:
❯ sudo snapper ls # | Type | Pre # | Date | User | Cleanup | Description | Userdata ---+--------+-------+---------------------------------+------+---------+--------------------------+--------- 0 | single | | | root | | current | 1 | pre | | Mon 18 Apr 2022 01:32:52 PM KST | root | number | /usr/bin/dnf install 0ad | 2 | post | 1 | Mon 18 Apr 2022 01:33:07 PM KST | root | number | /usr/bin/dnf install 0ad |
You'll notice that each snapshot has an ID listed. If you ever need to roll back to a previous state, you can use that ID to pick the state to roll back to. For example, if I want to revert to the state just before installing 0 A.D., I could run the following:
❯ sudo snapper --ambit classic rollback 1
As snapshots are read-only by default, when rolling back snapper actually creates a new read-writeable snapshot based off of the snapshot you specified, and sets that as the new bootable subvolume. You can see this by listing out the snapshots after running the above command. It should look something like this:
❯ sudo snapper ls # | Type | Pre # | Date | User | Cleanup | Description | Userdata ---+--------+-------+---------------------------------+------+---------+--------------------------+-------------- 0 | single | | | root | | current | 1 | pre | | Mon 18 Apr 2022 01:32:52 PM KST | root | number | /usr/bin/dnf install 0ad | 2 | post | 1 | Mon 18 Apr 2022 01:33:07 PM KST | root | number | /usr/bin/dnf install 0ad | 3 | single | | Mon 18 Apr 2022 01:38:52 PM KST | root | number | rollback backup | important=yes 4+ | single | | Mon 18 Apr 2022 01:38:52 PM KST | root | | writable copy of #1 |
Now when you
reboot, your system should be back to exactly what it looked like before installing the tool/module/thingy. I realize that my example of installing 0 A.D. wasn't a very useful use-case example, but you can imagine that this could be invaluable when installing something potentially unstable, or accidentally removing critical system level tools for example.
Adding snapshots for home, too
The way BTRFS snapshots work is that they do not include subvolumes inside other subvolumes when making snapshots, so your
/home directory is not included in snapshots for
This is actually a good thing, as it allows us to configure our home directory snapshots separately and in exactly the way we want. What's more, it allows you to revert your system to an earlier snapshot without losing any files stored in your
/home. Pretty neat, right?
Let's create another snapper config, this time for your home directory:
❯ sudo snapper -c home create-config /home
Let's add your user to the list of allowed users that are able to manage this configuration, so you don't have to use
sudo when interacting with your home snapshots. Here we also enable the
SYNC_ACL option which ensures file permissions are set to match whatever we configure through snapper for this particular configuration:
❯ sudo snapper -c home set-config SYNC_ACL=yes ALLOW_USERS=$USER
With that set, you should now be able to interact with snapper for your home directory without requiring
sudo. Let's try creating a manual snapshot now:
❯ snapper -c home create --description "Hello, snapshot!" ❯ snapper -c home ls # | Type | Pre # | Date | User | Cleanup | Description | Userdata ---+--------+-------+---------------------------------+------------+---------+------------------+--------- 0 | single | | | root | | current | 1 | single | | Mon 18 Apr 2022 02:07:50 PM KST | davejansen | | Hello, snapshot! |
Depending on your personal preferences, you might want to have snapper automatically create scheduled snapshots too. By default a configuration has hourly snapshots set, which we probably don't want for the root volume at least. Let's disable this.
Disabling hourly snapshots for root
/etc/snapper/configs/root with your favorite text editor and
sudo or root privileges, look for the
TIMELINE_CREATE value, and set this to
no, so it'll look like this:
# create hourly snapshots TIMELINE_CREATE="no"
Save your changes and close the file.
Customizing scheduled home snapshots
For your home directory, keeping hourly snapshots can have some nice benefits, so sticking with this default is probably a good thing. There are several additional settings you can tweak that control the number of hourly, daily, weekly, monthly, and yearly snapshots it wants to preserve. Keep in mind that the higher you set these numbers, the more space will be used by these snapshots over time. Here's one example of what this could look like:
# limits for timeline cleanup TIMELINE_MIN_AGE="1800" TIMELINE_LIMIT_HOURLY="12" TIMELINE_LIMIT_DAILY="7" TIMELINE_LIMIT_WEEKLY="2" TIMELINE_LIMIT_MONTHLY="6" TIMELINE_LIMIT_YEARLY="1"
Adjust these to your liking and save the file. Snapper will automatically pick up these changes.
Enabling scheduled snapshots and cleanup
In order for snapper to be able to run these scheduled tasks we need to enable the appropriate systemd timers:
❯ sudo systemctl enable --now snapper-timeline.timer ❯ sudo systemctl enable --now snapper-cleanup.timer
If you didn't enable any scheduled snapshots and just want the cleanup to happen automatically, you can enable only the second one.
We should now have a nice base Fedora setup with full snapshot and rollback support, even on the root level. While Fedora in general is a very stable experience –I've had absolutely no issues so far, it's such a pleasant experience!– there's always the possibility of a rogue tool or driver or configuration causing a ruckus. Having this extra layer of security is very nice for those kinds of cases.
I've been running this exact configuration on both my main machine as-well as my laptop, and it's been working great. I must admit that I've not yet had to actually fix a broken system by reverting to a previous state as I've not had either system break, but it definitely helped having this around as a safety net, especially when I was doing things like testing out custom kernels and whatnot a while ago.
When a new release of Fedora comes out, it's also nice to be able to upgrade your system and know that if anything breaks and either can't yet be fixed or you just don't have the time/interest to investigate, you're able to roll back to before the upgrade and continue with your working system, leaving that problem for another day.
I hope this is helpful to you. It know it might look a bit daunting with all these commands you have to run, but my hope is that I've written it out in an easy enough to follow way, with mostly copy/past-able commands. Of course I do hope that one day Fedora comes with a setup similar to this out of the box so you can just completely avoid having to configure such lower-level bits, but until that day comes, with just a little bit of elbow grease we're fortunately able to set it up ourselves.
I hope you'll enjoy your Fedora system!