Linux

Btrfs Snapshots with Snapper: Automatic System Rollback on Fedora and openSUSE

A bad dnf update can leave your Fedora system in a state where packages conflict, services refuse to start, or the kernel panics on boot. If your root filesystem is Btrfs, you can undo the entire transaction in seconds. Snapper creates copy-on-write snapshots before and after every package operation, and grub-btrfs lets you boot directly into any of those snapshots if the system won’t come up at all.

Original content from computingforgeeks.com - post 165233

This guide covers the full setup: configuring Snapper for automatic pre/post snapshots on Fedora 42, wiring it into DNF5 with the actions plugin, installing grub-btrfs so snapshots appear in the GRUB menu, and testing a real rollback by removing a package and restoring it. There’s also a comparison with openSUSE, which ships all of this out of the box. If you’re looking for a different backup strategy, see our guide on immutable backups with Restic and Borg.

Tested April 2026 on Fedora 42 (Cloud Edition, kernel 6.14.0-63.fc42.x86_64) with Snapper 0.11.0 and grub-btrfs 4.13

How Btrfs Snapshots Differ from LVM Snapshots

Both Btrfs and LVM can snapshot a filesystem, but the implementations are fundamentally different. Btrfs snapshots are metadata-only operations that complete instantly regardless of volume size. LVM snapshots allocate a separate COW device that degrades write performance proportionally to snapshot age. If you’re currently using LVM snapshots for backups, our LVM snapshot guide covers that workflow.

FeatureBtrfs SnapshotsLVM Snapshots
Creation timeInstant (metadata-only)Fast but requires COW device allocation
Performance impactNegligibleWrite penalty increases with snapshot age
Space usageShared blocks, grows only as data changesFixed-size COW device, overflows kill the snapshot
Rollback methodsnapper undochange or boot into snapshotlvconvert –merge (requires reboot)
Number of snapshotsHundreds with minimal overheadEach snapshot needs its own COW device
GranularityPer-subvolumePer-logical-volume
Built into filesystemYesNo (block-level, filesystem unaware)

The practical advantage is that you can keep dozens of Btrfs snapshots with almost no performance cost, which makes automatic pre/post snapshots on every DNF transaction viable.

Fedora 42 Default Btrfs Layout

Fedora has shipped Btrfs as the default filesystem since Fedora 33. The installer creates three subvolumes on the root partition, each mounted at a different path. Understanding this layout matters because Snapper operates on subvolumes, not mount points.

List the existing subvolumes:

sudo btrfs subvolume list /

Fedora 42 creates three subvolumes by default:

ID 256 gen 21 top level 5 path root
ID 257 gen 14 top level 5 path home
ID 258 gen 22 top level 5 path var

Check how they’re mounted:

mount | grep btrfs

Each subvolume is mounted with compression and async discard enabled:

/dev/vda4 on / type btrfs (rw,relatime,seclabel,compress=zstd:1,discard=async,space_cache=v2,subvolid=256,subvol=/root)
/dev/vda4 on /home type btrfs (rw,relatime,seclabel,compress=zstd:1,discard=async,space_cache=v2,subvolid=257,subvol=/home)
/dev/vda4 on /var type btrfs (rw,relatime,seclabel,compress=zstd:1,discard=async,space_cache=v2,subvolid=258,subvol=/var)

The root subvolume (ID 256) holds /, which is where DNF installs packages. That’s the subvolume we’ll configure Snapper to snapshot. The home subvolume gets its own config for user data protection.

Install and Configure Snapper

Snapper is in the default Fedora repos. Install it along with the DNF5 actions plugin, which we’ll need later for automatic pre/post snapshots:

sudo dnf install -y snapper libdnf5-plugin-actions

Create a Snapper configuration for the root subvolume:

sudo snapper -c root create-config /

This creates the /.snapshots directory and a new Snapper config file at /etc/snapper/configs/root. Do the same for /home if you want user data snapshots:

sudo snapper -c home create-config /home

Verify both configs exist:

sudo snapper list-configs

Both subvolumes should appear:

Config │ Subvolume
───────┼──────────
home   │ /home
root   │ /

Enable Timeline Snapshots

Timeline snapshots are hourly automatic snapshots that Snapper creates and cleans up based on retention rules. Enable them for the root config:

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

The timeline timer creates snapshots, and the cleanup timer deletes old ones according to the retention policy.

Set Retention Limits

The default retention is generous to a fault. On a server with limited disk, tighten it. Edit the root config:

sudo vi /etc/snapper/configs/root

Set these values (adjust to your disk size):

TIMELINE_CREATE=yes
TIMELINE_CLEANUP=yes
TIMELINE_LIMIT_HOURLY=5
TIMELINE_LIMIT_DAILY=7
TIMELINE_LIMIT_WEEKLY=0
TIMELINE_LIMIT_MONTHLY=0
SPACE_LIMIT=0.5
FREE_LIMIT=0.2
NUMBER_CLEANUP=yes
NUMBER_LIMIT=50

SPACE_LIMIT=0.5 means Snapper will delete snapshots if they consume more than 50% of the filesystem. FREE_LIMIT=0.2 triggers cleanup when free space drops below 20%. NUMBER_LIMIT=50 caps the total number of numbered snapshots (the pre/post type that DNF creates). These limits prevent snapshots from silently filling your disk.

SELinux Context for Snapshots

Fedora runs SELinux in enforcing mode. Snapper’s .snapshots directory needs the correct context, or restoring files from snapshots may fail with permission denied errors. The context is usually inherited correctly, but verify it:

ls -Zd /.snapshots

The context should show system_u:object_r:snapperd_data_t:s0. If it shows unlabeled_t instead, restore it:

sudo restorecon -rv /.snapshots

Take a manual test snapshot to confirm everything works:

sudo snapper -c root create -d "Manual test snapshot"

List snapshots to see it:

sudo snapper -c root list

Snapshot #1 should appear with your description and the current timestamp.

DNF5 Pre/Post Snapshots with the Actions Plugin

On openSUSE, zypper talks to Snapper natively. On Fedora, the old python3-dnf-plugin-snapper broke when Fedora 41 switched from DNF4 to DNF5. The replacement is the libdnf5-plugin-actions package, which lets you hook shell commands into DNF5 transaction events.

Create the actions file:

sudo vi /etc/dnf/libdnf5-plugins/actions.d/snapper.actions

Add the following content:

pre_transaction::::/usr/bin/sh -c echo\ "tmp.cmd=$(ps\ -o\ command\ --no-headers\ -p\ '${pid}')"
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre=$(snapper\ -c\ root\ create\ -c\ number\ -t\ pre\ -p\ -d\ '${tmp.cmd}')"
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre}"\ ]\ &&\ snapper\ -c\ root\ create\ -c\ number\ -t\ post\ --pre-number\ "${tmp.snapper_pre}"\ -d\ "${tmp.cmd}"

The first line captures the DNF command that triggered the transaction. The second creates a pre-transaction snapshot and stores its number. The third line runs after the transaction completes and creates the matching post snapshot, linking it to the pre snapshot number. The -c number flag marks both snapshots for the number cleanup algorithm.

Test it by installing a package:

sudo dnf install -y htop

Check whether Snapper captured the transaction:

sudo snapper -c root list

The pre/post pair should appear with the exact DNF command as the description:

 # │ Type   │ Pre # │ Date                            │ User │ Cleanup │ Description          │ Userdata
──┼────────┼───────┼─────────────────────────────────┼──────┼─────────┼──────────────────────┼─────────
0 │ single │       │                                 │ root │         │ current              │
1 │ single │       │ Fri 03 Apr 2026 04:20:23 PM UTC │ root │         │ Manual test snapshot │
2 │ pre    │       │ Fri 03 Apr 2026 04:21:05 PM UTC │ root │ number  │ dnf install -y htop  │
3 │ post   │     2 │ Fri 03 Apr 2026 04:21:05 PM UTC │ root │ number  │ dnf install -y htop  │

Snapshot #2 is the pre (filesystem state before htop was installed), and #3 is the post (state after). The Pre # column on #3 links it to #2, so Snapper knows they’re a pair.

Install grub-btrfs for Snapshot Boot

Rolling back from a running system with snapper undochange works well for package removals and config changes. But if a kernel update breaks boot entirely, you need to select a snapshot from the GRUB menu before the system even starts. That’s what grub-btrfs provides.

grub-btrfs is not in the Fedora repos, so build it from source. Install the build dependencies first:

sudo dnf install -y git make

Clone the repository and install:

git clone https://github.com/Antynea/grub-btrfs.git
cd grub-btrfs
sudo make install

Fedora uses grub2-mkconfig instead of grub-mkconfig, and the GRUB directory is /boot/grub2 instead of /boot/grub. Configure grub-btrfs for Fedora’s layout:

sudo vi /etc/default/grub-btrfs/config

Set these three variables:

GRUB_BTRFS_GRUB_DIRNAME="/boot/grub2"
GRUB_BTRFS_MKCONFIG=/usr/sbin/grub2-mkconfig
GRUB_BTRFS_SCRIPT_CHECK=grub2-script-check

Regenerate the GRUB configuration:

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

The output should list every snapshot it found:

Found snapshot: 2026-04-03 16:21:57 | root/.snapshots/5/snapshot | post   | dnf install -y git make |
Found snapshot: 2026-04-03 16:21:55 | root/.snapshots/4/snapshot | pre    | dnf install -y git make |
Found snapshot: 2026-04-03 16:21:05 | root/.snapshots/3/snapshot | post   | dnf install -y htop     |
Found 5 snapshot(s)

On reboot, a new “Fedora Linux snapshots” submenu appears in GRUB. Each snapshot is listed with its date and description. Selecting one boots a read-only view of the filesystem at that point in time, which is useful for recovering files or verifying what changed.

Enable the grub-btrfs daemon so new snapshots are automatically added to GRUB without running grub2-mkconfig manually each time:

sudo systemctl enable --now grub-btrfsd

The daemon watches /.snapshots for changes and regenerates the GRUB submenu automatically.

Break Something and Roll It Back

The real test of any snapshot system is recovering from damage. We’ll remove bash-completion (a package that deletes over a thousand files) and then undo the change using Snapper.

Remove the package:

sudo dnf remove -y bash-completion

The actions plugin creates snapshots #6 (pre) and #7 (post) automatically:

sudo snapper -c root list

The new pair appears at the bottom:

6 │ pre    │       │ Fri 03 Apr 2026 04:26:30 PM UTC │ root │ number  │ dnf remove -y bash-completion │
7 │ post   │     6 │ Fri 03 Apr 2026 04:26:30 PM UTC │ root │ number  │ dnf remove -y bash-completion │

See exactly what changed between the pre and post snapshots:

sudo snapper status 6..7

Snapper shows every file that was deleted, modified, or created. The -..... prefix means deleted:

-..... /etc/bash_completion.d/000_bash_completion_compat.bash
-..... /etc/profile.d/bash_completion.sh
c..... /usr/lib/sysimage/libdnf5/nevras.toml

The full list is much longer (over a thousand deleted files). Now restore the pre-removal state:

sudo snapper undochange 6..7

Snapper restores every deleted file and reverts every modification:

create:1051 modify:8 delete:0

1,051 files recreated, 8 files reverted to their pre-removal state. The bash-completion package is back, and all its files are in place. No reboot required, no package manager involved. This is the power of filesystem-level rollback: it doesn’t care what removed the files or why. It just restores the snapshot.

One caveat: snapper undochange restores files on disk, but the RPM database still thinks the package is removed. Run dnf reinstall -y bash-completion afterward to sync the package manager’s state with reality. This is a Fedora-specific limitation because unlike openSUSE, the RPM database lives on a separate subvolume.

openSUSE: How It Compares

openSUSE Tumbleweed and Leap have had Snapper integration since 2012. If you’re choosing between Fedora and openSUSE specifically for snapshot rollback, the differences are worth knowing.

CapabilityFedora 42openSUSE Tumbleweed/Leap
Snapper pre-installedNo (dnf install required)Yes, configured out of the box
Package manager integrationManual (actions plugin file)Native zypper integration
GRUB snapshot bootgrub-btrfs from sourceBuilt into grub2-snapper-plugin
Rollback commandsnapper undochangesnapper rollback (full subvolume swap)
RPM database in snapshotNo (lives in /var, separate subvolume)Yes (included in root snapshot)
Boot into snapshot + make permanentManual processsnapper rollback sets new default boot
YaST Snapper moduleN/AGUI for browsing and restoring snapshots
Default subvolume layout3 subvolumes (root, home, var)20+ subvolumes for granular control

The biggest difference is rollback depth. On openSUSE, snapper rollback performs a true subvolume swap: it makes the snapshot the new default root and reboots into it. The RPM database, the kernel, everything rolls back together because they’re all in the same snapshot. On Fedora, snapper undochange is a file-level restore that doesn’t touch the bootloader or RPM database. Both approaches work, but openSUSE’s is more integrated.

That said, Fedora’s approach gives you more flexibility. You can selectively undo specific transactions without reverting everything that happened after them, which is useful when only one DNF operation caused problems.

Snapshot Space Management

Snapshots are cheap to create but expensive to keep. Every file modification after a snapshot is taken means the old data block is preserved instead of being freed. Over weeks, this adds up.

Snapper’s Cleanup Algorithms

Snapper has three cleanup algorithms that run via the snapper-cleanup.timer:

  • number keeps only the most recent N snapshots of the numbered type (pre/post pairs from DNF). Controlled by NUMBER_LIMIT
  • timeline thins out hourly snapshots over time, keeping more recent ones and fewer old ones. Controlled by TIMELINE_LIMIT_HOURLY, TIMELINE_LIMIT_DAILY, etc.
  • empty-pre-post deletes pre/post pairs where nothing actually changed between them

The retention settings from earlier keep 5 hourly, 7 daily, and a max of 50 numbered snapshots. On a typical Fedora workstation or server that runs dnf update weekly, this translates to roughly 2 months of numbered snapshot history.

Check Actual Disk Usage

Standard df shows total Btrfs usage but doesn’t break down how much space snapshots consume. Use the Btrfs-specific command:

sudo btrfs filesystem usage /

Look at the “Used” line under “Data” to see actual consumption. For a per-snapshot breakdown, you technically need Btrfs quota groups (qgroups), but enabling qgroups on a production system is a bad idea. Qgroups cause significant performance degradation on filesystems with many snapshots because the kernel must track every block reference. The Btrfs developers themselves recommend avoiding qgroups unless you absolutely need per-subvolume accounting.

Manual Cleanup

If disk space is tight, delete specific snapshots by number:

sudo snapper -c root delete 2 3

This removes the pre/post pair from the htop install. To delete a range:

sudo snapper -c root delete 1-5

Deleted snapshots don’t free space immediately. Btrfs reclaims blocks asynchronously in the background. Run btrfs filesystem sync / to flush pending operations, then check usage again.

What Snapshots Don’t Replace

Btrfs snapshots are not backups. They live on the same physical disk as the original data. A disk failure takes out both the live filesystem and every snapshot. Use snapshots for fast rollback after bad updates or config changes. Use actual backups (Restic, Borg, rsync to a different machine) for disaster recovery.

Frequently Asked Questions

Can Snapper roll back a kernel update on Fedora?

snapper undochange can restore the previous kernel’s files, but the bootloader still points to the new kernel. For kernel rollback, boot into a pre-update snapshot via the GRUB menu (provided by grub-btrfs), then remove the broken kernel from the live snapshot. Alternatively, use grubby --set-default to switch the default kernel entry without touching snapshots.

Do snapshots slow down Btrfs?

Creating and deleting snapshots is nearly instant. The performance cost comes from keeping many old snapshots while the filesystem is actively written to, because Btrfs must preserve old data blocks instead of overwriting them. With the retention limits set earlier (50 numbered, 5 hourly, 7 daily), the overhead is negligible on most workloads. Thousands of snapshots on a heavily written filesystem is where you’d notice it.

Does snapper undochange work across subvolume boundaries?

No. Each Snapper config operates on one subvolume. On Fedora, /var is a separate subvolume, so changes to files under /var (like the RPM database) are not captured in the root config’s snapshots. This is why you need to run dnf reinstall after a file-level undo to sync the package database. On openSUSE, the subvolume layout is designed to avoid this problem.

Related Articles

Virtualization Install VirtualBox extension pack using VBoxManage Debian Install Lightworks Video Editor on Ubuntu / Linux Mint / Debian AlmaLinux Install MySQL 8.4 LTS on Rocky Linux 10 / AlmaLinux 10 Linux Mint Install Linux Mint 22.3 Step by Step (with Screenshots)

Leave a Comment

Press ESC to close