Fedora Workstation uses Btrfs as the default root filesystem. A stock Anaconda install creates two subvolumes: root (mounted at /) and home (mounted at /home). On that default, /var lives inside the root subvolume rather than on its own, so a root snapshot already captures it. That layout makes Snapper a natural fit for system rollback: capture a snapshot before every risky package operation, and if anything breaks, boot back into the snapshot from the GRUB menu. With grub-btrfs handling the menu generation automatically, you get openSUSE-style rollback on Fedora without writing a single shell hook.
This guide sets up Snapper for the root subvolume on a fresh Fedora install, creates pre- and post-install snapshots around a real dnf transaction, configures grub-btrfs to expose those snapshots in the boot menu, and walks through the actual rollback workflow. Every command was executed on a real Fedora VM; the output and screenshots are what you will see when you follow along.
Re-tested September 2026 on Fedora 44 (kernel 7.0.8-200.fc44, snapper 0.13.0, btrfs-progs 6.19.1, grub-btrfs 0.0.git.275 from the kylegospo COPR). Package names were re-checked against the live Fedora 44, 43, and 42 repositories: snapper is 0.13.0 on Fedora 44 and 0.11.0 on the two older branches, and the snapper CLI subset used here is stable across all three.

Verify the Btrfs layout on Fedora
Before touching Snapper, confirm that Fedora installed itself onto Btrfs and not onto ext4 or XFS. The Anaconda installer offers both filesystems in current release cycles. Cloud Base and Workstation use Btrfs by default; Server and custom partitioning are the cases that flip to other filesystems.
findmnt /
sudo btrfs filesystem show
sudo btrfs subvolume list /
The findmnt output should show btrfs with a subvol=/root mount option. The subvolume list will include at least root and home. Some install paths add more: custom partitioning and the Atomic/ostree spins (Silverblue, Kinoite) carve out separate var and boot subvolumes, and systemd auto-creates var/lib/machines, so your exact list may have more entries than a stock Workstation install (which shows just root and home). Either way Snapper targets the root subvolume, so the steps below are identical. If you do not see Btrfs here, the rest of this guide does not apply: stop and either reinstall on Btrfs or use LVM snapshots instead.
Install Snapper and create a config for /
Snapper is in the official Fedora repo. There is no snapper-tools package on Fedora at all; that name belongs to openSUSE, and asking dnf for it fails the whole transaction with “No match for argument: snapper-tools” so nothing at all gets installed. inotify-tools is in the Fedora repo and is needed further down, where the GRUB integration watches /.snapshots for new snapshots.
sudo dnf install -y snapper inotify-tools
Create a Snapper configuration named root for the root subvolume:
sudo snapper -c root create-config /
sudo snapper list-configs
create-config generates /etc/snapper/configs/root, mounts a hidden .snapshots subvolume under /, and starts the snapper-timeline and snapper-cleanup timers. From this point on, hourly timeline snapshots accumulate automatically.
Create pre- and post-install snapshots around a dnf transaction
Manual snapshots before and after a risky package operation are the bread and butter of the workflow. Create a baseline, install a package, then create a follow-up snapshot:
sudo snapper -c root create --description "pre-flatpak-install"
sudo dnf install -y cowsay
sudo snapper -c root create --description "post-cowsay-install"
sudo snapper -c root list
The command output is shown above.

The list shows every snapshot with its ID, type (single, pre, post), date, description, and userdata. Number 0 is always the live filesystem. Pre/post pairs are linked together by Pre # so you can see exactly what changed for any given dnf transaction.
See what changed between two snapshots
Two commands you will use often:
sudo snapper -c root status 1..2
sudo snapper -c root diff 1..2 /usr/bin/cowsay
The command output is shown above.

The first column of status output is a five-character flag string. + means “new file in the newer snapshot”, - “deleted”, c “content changed”, p “permissions changed”, g “gid changed”, u “uid changed”. For a typical dnf install you will see hundreds of + entries; for a quick config change just one or two c lines.
Selective rollback for a single file (without rebooting):
sudo snapper -c root undochange 1..2 /etc/myconfig.conf
This restores myconfig.conf from snapshot 1 onto the live filesystem and leaves everything else untouched. Use it for “I edited a file, broke the daemon, and want just that file back” scenarios; reach for full rollback (next section) for “the whole system needs to go back to before this dnf transaction”.
Install grub-btrfs for boot-menu rollback
grub-btrfs hooks into Snapper and writes a “Fedora Linux snapshots” submenu into your GRUB config. From there you can boot directly into any snapshot and roll back from inside that snapshot.
grub-btrfs is not in Fedora’s main repo. Enable the well-maintained COPR by kylegospo and install:
sudo dnf copr enable -y kylegospo/grub-btrfs
sudo dnf install -y grub-btrfs
Run the snapshot detector once manually to confirm it sees your Snapper snapshots, then regenerate the GRUB config:
sudo /etc/grub.d/41_snapshots-btrfs
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
The command output is shown above.

To make grub-btrfs update the menu automatically every time Snapper creates a new snapshot, enable the path unit:
systemctl cat grub-btrfs.path | grep -E 'BindsTo|Requires|PathModified'
Read that output before you enable anything, because the unit as shipped cannot start on Fedora. It carries Requires, After, and BindsTo on \x2esnapshots.mount, the mount unit for /.snapshots. Fedora’s snapper creates /.snapshots as a btrfs subvolume inside the root filesystem instead of a separate mount, so that unit does not exist and systemctl enable --now grub-btrfs.path ends with “Failed to start grub-btrfs.path: Unit \x2esnapshots.mount not found”. A drop-in cannot rescue it either: BindsTo is one of the few settings systemd will not let you reset from a drop-in file.
Write your own path unit instead. It watches the same directory and triggers the same grub-btrfs.service the package already installed, without the mount dependency:
sudo tee /etc/systemd/system/grub-btrfs-watch.path >/dev/null <<'EOF'
[Unit]
Description=Watch /.snapshots and refresh the GRUB snapshot menu
[Path]
PathModified=/.snapshots
Unit=grub-btrfs.service
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now grub-btrfs-watch.path
Prove it works by deleting the generated menu and taking a snapshot:
sudo rm -f /boot/grub2/grub-btrfs.cfg
sudo snapper -c root create --description "watch-test"
sudo journalctl -u grub-btrfs.service -n 5 --no-pager
ls -l /boot/grub2/grub-btrfs.cfg
The journal logs “Found N snapshot(s)” and grub-btrfs.cfg is back within a few seconds.
Boot from a snapshot and roll back the system
Reboot. Hold Shift at the BIOS POST screen to display the GRUB menu if you set a zero timeout, or it appears automatically with the default Fedora settings. You will see a new Fedora Linux snapshots submenu with one entry per Snapper snapshot.
Pick a snapshot. The system boots into a read-only snapshot mount; that read-only mode is the safety feature that lets you verify “yes, this snapshot does have the working state I expected” before committing to the rollback. To make the rollback permanent (the live root subvolume becomes the snapshot’s contents):
sudo btrfs subvolume get-default /
sudo snapper -c root rollback 1
sudo systemctl reboot
The command output is shown above.

snapper rollback N does three things in one transaction: creates a read-only snapshot of the broken current state (so you can come back from your rollback if needed), creates a writable snapshot from snapshot N, and sets the new writable snapshot as the default subvolume. After reboot, the system is back in the state of snapshot N. The dnf transaction that caused the problem is gone.
Automatic timeline snapshots and cleanup
Snapper’s timeline timer creates a snapshot every hour and runs a cleanup that prunes old ones. The defaults are conservative but on a busy desktop they fill up a couple of GB per week. Tune them in /etc/snapper/configs/root:
TIMELINE_CREATE="yes"
TIMELINE_CLEANUP="yes"
TIMELINE_MIN_AGE="1800"
TIMELINE_LIMIT_HOURLY="10"
TIMELINE_LIMIT_DAILY="10"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="10"
TIMELINE_LIMIT_YEARLY="10"
NUMBER_LIMIT="50-100"
NUMBER_LIMIT_IMPORTANT="10-20"
SPACE_LIMIT="0.5"
SPACE_LIMIT="0.5" means “snapshots may use up to 50 percent of the filesystem”. On a 40 GB root that is plenty. After editing the config, restart the cleanup timer so it picks up the new policy:
sudo systemctl restart snapper-cleanup.timer
sudo systemctl list-timers | grep snapper
Auto-snapshot every dnf transaction
Fedora has no snapper plugin for dnf5. The python3-dnf-plugin-snapper package is still in the repo, but it is a dnf4 plugin: it installs into the Python dnf-plugins directory while dnf5 loads compiled plugins from somewhere else entirely, so transactions run and not one snapshot appears. What does work is the actions plugin, which runs commands of your choosing around every transaction. Install it and hand it two small scripts:
sudo dnf install -y libdnf5-plugin-actions
sudo tee /usr/local/bin/dnf-snapper-pre >/dev/null <<'EOF'
#!/bin/bash
snapper -c root create -t pre -p -c number -d "dnf transaction" > /run/snapper-dnf-pre
EOF
sudo tee /usr/local/bin/dnf-snapper-post >/dev/null <<'EOF'
#!/bin/bash
[ -s /run/snapper-dnf-pre ] || exit 0
snapper -c root create -t post --pre-number "$(cat /run/snapper-dnf-pre)" -c number -d "dnf transaction"
rm -f /run/snapper-dnf-pre
EOF
sudo chmod 755 /usr/local/bin/dnf-snapper-pre /usr/local/bin/dnf-snapper-post
sudo tee /etc/dnf/libdnf5-plugins/actions.d/snapper.actions >/dev/null <<'EOF'
pre_transaction::::/usr/local/bin/dnf-snapper-pre
post_transaction::::/usr/local/bin/dnf-snapper-post
EOF
Now install something the system does not already have and look at the list:
sudo dnf install -y cowsay
sudo snapper -c root list | tail -4
Two new rows appear, one of type pre and one of type post whose “Pre #” column points back at it. Every later dnf install, dnf upgrade, and dnf remove produces the same pair. Roll back any single transaction with snapper rollback <post-snapshot-id>. A command with nothing to do, such as installing a package that is already present, creates no snapshots, because no transaction runs.
What about /home, /var, and /boot
Fedora keeps /home on its own Btrfs subvolume, so a root rollback never touches your downloads, browser history, or dotfiles. /var is the one to watch. On a stock Workstation install it lives inside the root subvolume, which means a snapper rollback of / also reverts /var, including systemd journals, container images, and database storage under /var/lib. Confirm which layout you have with sudo btrfs subvolume list /. If you want snapshots of /home too, create a separate Snapper config:
sudo snapper -c home create-config /home
sudo snapper -c home create --description "baseline"
sudo snapper list-configs
If your install does have /var on a separate subvolume (custom partitioning or an Atomic spin), a root rollback leaves it untouched, which is usually what you want for volatile data. If /var is part of root (the Workstation default), every root rollback rewinds it too, so the safe habit is to snapshot / right before a risky change instead of restoring a day-old snapshot that would also throw away today’s container images and database writes. The recommended pattern is: snapshot / aggressively before risky operations, snapshot /home daily for safety, and know which side of that /var split your system is on before you roll back.
Where this fits in the Fedora 44 Workstation series
Snapper plus grub-btrfs is the per-host rollback layer. For shipping multiple Fedora boxes from a known-good baseline you want a separate strategy: golden images, Ansible playbooks, or rpm-ostree if you can switch to Silverblue. The companion Fedora 44 post-install guide in this series covers where Snapper fits next to firewalld, SELinux, and DNS over TLS. For openSUSE and other multi-distro Btrfs snapshot setups, the existing cross-distro Btrfs Snapshots with Snapper guide stays the reference.
A fresh install of Fedora 44 does not create a /var subvolume in my case.
You are right. A stock Fedora Workstation install creates only root and home subvolumes, with /var sitting inside root; a separate var subvolume only shows up with custom partitioning or on the Atomic spins like Silverblue. The catch worth knowing: on that default layout, a snapper rollback of / also reverts /var (journals, container images, databases), since /var rides inside the root subvolume. I have updated the guide to cover both layouts and how to check which one you have.
(my) Fedora 44 does not have snapper-tools and inotify-tools in its standard repos.
You are right about snapper-tools and the article is fixed, thank you. There is no snapper-tools package in Fedora at all, on 44, 43 or 42; that name belongs to openSUSE. The reason inotify-tools looked missing too is that dnf refuses the entire transaction with
No match for argument: snapper-tools, so nothing on that line gets installed. inotify-tools is in the Fedora 44 repository (4.23.9.0-6.fc44) and installs cleanly once the bad name is gone. The command now readssudo dnf install -y snapper inotify-tools.Re-running the whole guide on a Fedora 44 VM to check this turned up two more problems, both now corrected in the article:
–
python3-dnf-plugin-snapperis a dnf4 plugin. Under dnf5 it is never loaded, so transactions ran and not one pre/post snapshot appeared. The article now useslibdnf5-plugin-actionswith two short scripts, which does produce the pre and post pair around every transaction.– The
grub-btrfs.pathunit from the COPR cannot start on Fedora. It carries BindsTo on\x2esnapshots.mount, and Fedora’s snapper creates/.snapshotsas a btrfs subvolume rather than a separate mount, so the unit fails withUnit \x2esnapshots.mount not found. A drop-in cannot fix it, because BindsTo is one of the settings systemd will not let you reset. The article now installs a small path unit of its own that triggers the same service, and it does regenerate the boot menu on every new snapshot.