{"id":165233,"date":"2026-04-03T20:15:15","date_gmt":"2026-04-03T17:15:15","guid":{"rendered":"https:\/\/computingforgeeks.com\/?p=165233"},"modified":"2026-04-03T20:15:15","modified_gmt":"2026-04-03T17:15:15","slug":"btrfs-snapshots-snapper-automatic-rollback-fedora-opensuse","status":"publish","type":"post","link":"https:\/\/computingforgeeks.com\/btrfs-snapshots-snapper-automatic-rollback-fedora-opensuse\/","title":{"rendered":"Btrfs Snapshots with Snapper: Automatic System Rollback on Fedora and openSUSE"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">A bad <code>dnf update<\/code> 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&#8217;t come up at all.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;s also a comparison with openSUSE, which ships all of this out of the box. If you&#8217;re looking for a different backup strategy, see our guide on <a href=\"https:\/\/computingforgeeks.com\/immutable-backups-ransomware-proof-linux-minio-restic-borg\/\" target=\"_blank\" rel=\"noreferrer noopener\">immutable backups with Restic and Borg<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>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<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Btrfs Snapshots Differ from LVM Snapshots<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;re currently using LVM snapshots for backups, our <a href=\"https:\/\/computingforgeeks.com\/lvm-snapshots-consistent-server-backups-linux\/\" target=\"_blank\" rel=\"noreferrer noopener\">LVM snapshot guide<\/a> covers that workflow.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Feature<\/th><th>Btrfs Snapshots<\/th><th>LVM Snapshots<\/th><\/tr><\/thead><tbody><tr><td>Creation time<\/td><td>Instant (metadata-only)<\/td><td>Fast but requires COW device allocation<\/td><\/tr><tr><td>Performance impact<\/td><td>Negligible<\/td><td>Write penalty increases with snapshot age<\/td><\/tr><tr><td>Space usage<\/td><td>Shared blocks, grows only as data changes<\/td><td>Fixed-size COW device, overflows kill the snapshot<\/td><\/tr><tr><td>Rollback method<\/td><td><code>snapper undochange<\/code> or boot into snapshot<\/td><td>lvconvert &#8211;merge (requires reboot)<\/td><\/tr><tr><td>Number of snapshots<\/td><td>Hundreds with minimal overhead<\/td><td>Each snapshot needs its own COW device<\/td><\/tr><tr><td>Granularity<\/td><td>Per-subvolume<\/td><td>Per-logical-volume<\/td><\/tr><tr><td>Built into filesystem<\/td><td>Yes<\/td><td>No (block-level, filesystem unaware)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fedora 42 Default Btrfs Layout<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">List the existing subvolumes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo btrfs subvolume list \/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Fedora 42 creates three subvolumes by default:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ID 256 gen 21 top level 5 path root\nID 257 gen 14 top level 5 path home\nID 258 gen 22 top level 5 path var<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check how they&#8217;re mounted:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mount | grep btrfs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each subvolume is mounted with compression and async discard enabled:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/dev\/vda4 on \/ type btrfs (rw,relatime,seclabel,compress=zstd:1,discard=async,space_cache=v2,subvolid=256,subvol=\/root)\n\/dev\/vda4 on \/home type btrfs (rw,relatime,seclabel,compress=zstd:1,discard=async,space_cache=v2,subvolid=257,subvol=\/home)\n\/dev\/vda4 on \/var type btrfs (rw,relatime,seclabel,compress=zstd:1,discard=async,space_cache=v2,subvolid=258,subvol=\/var)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>root<\/code> subvolume (ID 256) holds <code>\/<\/code>, which is where DNF installs packages. That&#8217;s the subvolume we&#8217;ll configure Snapper to snapshot. The <code>home<\/code> subvolume gets its own config for user data protection.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install and Configure Snapper<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Snapper is in the default Fedora repos. Install it along with the DNF5 actions plugin, which we&#8217;ll need later for automatic pre\/post snapshots:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf install -y snapper libdnf5-plugin-actions<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create a Snapper configuration for the root subvolume:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper -c root create-config \/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This creates the <code>\/.snapshots<\/code> directory and a new Snapper config file at <code>\/etc\/snapper\/configs\/root<\/code>. Do the same for <code>\/home<\/code> if you want user data snapshots:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper -c home create-config \/home<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Verify both configs exist:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper list-configs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Both subvolumes should appear:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Config \u2502 Subvolume\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nhome   \u2502 \/home\nroot   \u2502 \/<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Enable Timeline Snapshots<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Timeline snapshots are hourly automatic snapshots that Snapper creates and cleans up based on retention rules. Enable them for the root config:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl enable --now snapper-timeline.timer\nsudo systemctl enable --now snapper-cleanup.timer<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The timeline timer creates snapshots, and the cleanup timer deletes old ones according to the retention policy.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Set Retention Limits<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The default retention is generous to a fault. On a server with limited disk, tighten it. Edit the root config:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo vi \/etc\/snapper\/configs\/root<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Set these values (adjust to your disk size):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>TIMELINE_CREATE=yes\nTIMELINE_CLEANUP=yes\nTIMELINE_LIMIT_HOURLY=5\nTIMELINE_LIMIT_DAILY=7\nTIMELINE_LIMIT_WEEKLY=0\nTIMELINE_LIMIT_MONTHLY=0\nSPACE_LIMIT=0.5\nFREE_LIMIT=0.2\nNUMBER_CLEANUP=yes\nNUMBER_LIMIT=50<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>SPACE_LIMIT=0.5<\/code> means Snapper will delete snapshots if they consume more than 50% of the filesystem. <code>FREE_LIMIT=0.2<\/code> triggers cleanup when free space drops below 20%. <code>NUMBER_LIMIT=50<\/code> caps the total number of numbered snapshots (the pre\/post type that DNF creates). These limits prevent snapshots from silently filling your disk.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">SELinux Context for Snapshots<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Fedora runs SELinux in enforcing mode. Snapper&#8217;s <code>.snapshots<\/code> 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:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ls -Zd \/.snapshots<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The context should show <code>system_u:object_r:snapperd_data_t:s0<\/code>. If it shows <code>unlabeled_t<\/code> instead, restore it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo restorecon -rv \/.snapshots<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Take a manual test snapshot to confirm everything works:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper -c root create -d \"Manual test snapshot\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">List snapshots to see it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper -c root list<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Snapshot #1 should appear with your description and the current timestamp.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">DNF5 Pre\/Post Snapshots with the Actions Plugin<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On openSUSE, zypper talks to Snapper natively. On Fedora, the old <code>python3-dnf-plugin-snapper<\/code> broke when Fedora 41 switched from DNF4 to DNF5. The replacement is the <code>libdnf5-plugin-actions<\/code> package, which lets you hook shell commands into DNF5 transaction events.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Create the actions file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo vi \/etc\/dnf\/libdnf5-plugins\/actions.d\/snapper.actions<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add the following content:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pre_transaction::::\/usr\/bin\/sh -c echo\\ \"tmp.cmd=$(ps\\ -o\\ command\\ --no-headers\\ -p\\ '${pid}')\"\npre_transaction::::\/usr\/bin\/sh -c echo\\ \"tmp.snapper_pre=$(snapper\\ -c\\ root\\ create\\ -c\\ number\\ -t\\ pre\\ -p\\ -d\\ '${tmp.cmd}')\"\npost_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}\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">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 <code>-c number<\/code> flag marks both snapshots for the number cleanup algorithm.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Test it by installing a package:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf install -y htop<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check whether Snapper captured the transaction:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper -c root list<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The pre\/post pair should appear with the exact DNF command as the description:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> # \u2502 Type   \u2502 Pre # \u2502 Date                            \u2502 User \u2502 Cleanup \u2502 Description          \u2502 Userdata\n\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n0 \u2502 single \u2502       \u2502                                 \u2502 root \u2502         \u2502 current              \u2502\n1 \u2502 single \u2502       \u2502 Fri 03 Apr 2026 04:20:23 PM UTC \u2502 root \u2502         \u2502 Manual test snapshot \u2502\n2 \u2502 pre    \u2502       \u2502 Fri 03 Apr 2026 04:21:05 PM UTC \u2502 root \u2502 number  \u2502 dnf install -y htop  \u2502\n3 \u2502 post   \u2502     2 \u2502 Fri 03 Apr 2026 04:21:05 PM UTC \u2502 root \u2502 number  \u2502 dnf install -y htop  \u2502<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Snapshot #2 is the pre (filesystem state before htop was installed), and #3 is the post (state after). The <code>Pre #<\/code> column on #3 links it to #2, so Snapper knows they&#8217;re a pair.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install grub-btrfs for Snapshot Boot<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Rolling back from a running system with <code>snapper undochange<\/code> 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&#8217;s what grub-btrfs provides.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">grub-btrfs is not in the Fedora repos, so build it from source. Install the build dependencies first:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf install -y git make<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Clone the repository and install:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git clone https:\/\/github.com\/Antynea\/grub-btrfs.git\ncd grub-btrfs\nsudo make install<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Fedora uses <code>grub2-mkconfig<\/code> instead of <code>grub-mkconfig<\/code>, and the GRUB directory is <code>\/boot\/grub2<\/code> instead of <code>\/boot\/grub<\/code>. Configure grub-btrfs for Fedora&#8217;s layout:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo vi \/etc\/default\/grub-btrfs\/config<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Set these three variables:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GRUB_BTRFS_GRUB_DIRNAME=\"\/boot\/grub2\"\nGRUB_BTRFS_MKCONFIG=\/usr\/sbin\/grub2-mkconfig\nGRUB_BTRFS_SCRIPT_CHECK=grub2-script-check<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Regenerate the GRUB configuration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo grub2-mkconfig -o \/boot\/grub2\/grub.cfg<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The output should list every snapshot it found:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Found snapshot: 2026-04-03 16:21:57 | root\/.snapshots\/5\/snapshot | post   | dnf install -y git make |\nFound snapshot: 2026-04-03 16:21:55 | root\/.snapshots\/4\/snapshot | pre    | dnf install -y git make |\nFound snapshot: 2026-04-03 16:21:05 | root\/.snapshots\/3\/snapshot | post   | dnf install -y htop     |\nFound 5 snapshot(s)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On reboot, a new &#8220;Fedora Linux snapshots&#8221; 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enable the grub-btrfs daemon so new snapshots are automatically added to GRUB without running <code>grub2-mkconfig<\/code> manually each time:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl enable --now grub-btrfsd<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The daemon watches <code>\/.snapshots<\/code> for changes and regenerates the GRUB submenu automatically.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Break Something and Roll It Back<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The real test of any snapshot system is recovering from damage. We&#8217;ll remove <code>bash-completion<\/code> (a package that deletes over a thousand files) and then undo the change using Snapper.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Remove the package:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf remove -y bash-completion<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The actions plugin creates snapshots #6 (pre) and #7 (post) automatically:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper -c root list<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The new pair appears at the bottom:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>6 \u2502 pre    \u2502       \u2502 Fri 03 Apr 2026 04:26:30 PM UTC \u2502 root \u2502 number  \u2502 dnf remove -y bash-completion \u2502\n7 \u2502 post   \u2502     6 \u2502 Fri 03 Apr 2026 04:26:30 PM UTC \u2502 root \u2502 number  \u2502 dnf remove -y bash-completion \u2502<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">See exactly what changed between the pre and post snapshots:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper status 6..7<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Snapper shows every file that was deleted, modified, or created. The <code>-.....<\/code> prefix means deleted:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-..... \/etc\/bash_completion.d\/000_bash_completion_compat.bash\n-..... \/etc\/profile.d\/bash_completion.sh\nc..... \/usr\/lib\/sysimage\/libdnf5\/nevras.toml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The full list is much longer (over a thousand deleted files). Now restore the pre-removal state:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper undochange 6..7<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Snapper restores every deleted file and reverts every modification:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>create:1051 modify:8 delete:0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;t care what removed the files or why. It just restores the snapshot.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One caveat: <code>snapper undochange<\/code> restores files on disk, but the RPM database still thinks the package is removed. Run <code>dnf reinstall -y bash-completion<\/code> afterward to sync the package manager&#8217;s state with reality. This is a Fedora-specific limitation because unlike openSUSE, the RPM database lives on a separate subvolume.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">openSUSE: How It Compares<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">openSUSE Tumbleweed and Leap have had Snapper integration since 2012. If you&#8217;re choosing between Fedora and openSUSE specifically for snapshot rollback, the differences are worth knowing.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Capability<\/th><th>Fedora 42<\/th><th>openSUSE Tumbleweed\/Leap<\/th><\/tr><\/thead><tbody><tr><td>Snapper pre-installed<\/td><td>No (dnf install required)<\/td><td>Yes, configured out of the box<\/td><\/tr><tr><td>Package manager integration<\/td><td>Manual (actions plugin file)<\/td><td>Native zypper integration<\/td><\/tr><tr><td>GRUB snapshot boot<\/td><td>grub-btrfs from source<\/td><td>Built into grub2-snapper-plugin<\/td><\/tr><tr><td>Rollback command<\/td><td><code>snapper undochange<\/code><\/td><td><code>snapper rollback<\/code> (full subvolume swap)<\/td><\/tr><tr><td>RPM database in snapshot<\/td><td>No (lives in \/var, separate subvolume)<\/td><td>Yes (included in root snapshot)<\/td><\/tr><tr><td>Boot into snapshot + make permanent<\/td><td>Manual process<\/td><td><code>snapper rollback<\/code> sets new default boot<\/td><\/tr><tr><td>YaST Snapper module<\/td><td>N\/A<\/td><td>GUI for browsing and restoring snapshots<\/td><\/tr><tr><td>Default subvolume layout<\/td><td>3 subvolumes (root, home, var)<\/td><td>20+ subvolumes for granular control<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The biggest difference is rollback depth. On openSUSE, <code>snapper rollback<\/code> 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&#8217;re all in the same snapshot. On Fedora, <code>snapper undochange<\/code> is a file-level restore that doesn&#8217;t touch the bootloader or RPM database. Both approaches work, but openSUSE&#8217;s is more integrated.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That said, Fedora&#8217;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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Snapshot Space Management<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Snapper&#8217;s Cleanup Algorithms<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Snapper has three cleanup algorithms that run via the <code>snapper-cleanup.timer<\/code>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>number<\/strong> keeps only the most recent N snapshots of the numbered type (pre\/post pairs from DNF). Controlled by <code>NUMBER_LIMIT<\/code><\/li>\n<li><strong>timeline<\/strong> thins out hourly snapshots over time, keeping more recent ones and fewer old ones. Controlled by <code>TIMELINE_LIMIT_HOURLY<\/code>, <code>TIMELINE_LIMIT_DAILY<\/code>, etc.<\/li>\n<li><strong>empty-pre-post<\/strong> deletes pre\/post pairs where nothing actually changed between them<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">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 <code>dnf update<\/code> weekly, this translates to roughly 2 months of numbered snapshot history.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Check Actual Disk Usage<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Standard <code>df<\/code> shows total Btrfs usage but doesn&#8217;t break down how much space snapshots consume. Use the Btrfs-specific command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo btrfs filesystem usage \/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Look at the &#8220;Used&#8221; line under &#8220;Data&#8221; 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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Manual Cleanup<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If disk space is tight, delete specific snapshots by number:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper -c root delete 2 3<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This removes the pre\/post pair from the htop install. To delete a range:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snapper -c root delete 1-5<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Deleted snapshots don&#8217;t free space immediately. Btrfs reclaims blocks asynchronously in the background. Run <code>btrfs filesystem sync \/<\/code> to flush pending operations, then check usage again.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What Snapshots Don&#8217;t Replace<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently Asked Questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Can Snapper roll back a kernel update on Fedora?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>snapper undochange<\/code> can restore the previous kernel&#8217;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 <code>grubby --set-default<\/code> to switch the default kernel entry without touching snapshots.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Do snapshots slow down Btrfs?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;d notice it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Does snapper undochange work across subvolume boundaries?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Each Snapper config operates on one subvolume. On Fedora, <code>\/var<\/code> is a separate subvolume, so changes to files under <code>\/var<\/code> (like the RPM database) are not captured in the root config&#8217;s snapshots. This is why you need to run <code>dnf reinstall<\/code> after a file-level undo to sync the package database. On openSUSE, the subvolume layout is designed to avoid this problem.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 &#8230; <a title=\"Btrfs Snapshots with Snapper: Automatic System Rollback on Fedora and openSUSE\" class=\"read-more\" href=\"https:\/\/computingforgeeks.com\/btrfs-snapshots-snapper-automatic-rollback-fedora-opensuse\/\" aria-label=\"Read more about Btrfs Snapshots with Snapper: Automatic System Rollback on Fedora and openSUSE\">Read more<\/a><\/p>\n","protected":false},"author":3,"featured_media":165234,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[47,50],"tags":[681,282,209],"cfg_series":[],"class_list":["post-165233","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux","category-linux-tutorials","tag-fedora","tag-linux","tag-storage"],"_links":{"self":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/165233","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/comments?post=165233"}],"version-history":[{"count":1,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/165233\/revisions"}],"predecessor-version":[{"id":165237,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/165233\/revisions\/165237"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media\/165234"}],"wp:attachment":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media?parent=165233"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/categories?post=165233"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/tags?post=165233"},{"taxonomy":"cfg_series","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/cfg_series?post=165233"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}