{"id":167774,"date":"2026-05-20T14:09:44","date_gmt":"2026-05-20T11:09:44","guid":{"rendered":"https:\/\/computingforgeeks.com\/?p=167774"},"modified":"2026-06-02T22:45:23","modified_gmt":"2026-06-02T19:45:23","slug":"btrfs-snapper-grub-btrfs-fedora","status":"publish","type":"post","link":"https:\/\/computingforgeeks.com\/btrfs-snapper-grub-btrfs-fedora\/","title":{"rendered":"Setup Btrfs Snapshots with Snapper on Fedora 44 \/ 43 \/ 42"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Fedora Workstation uses Btrfs as the default root filesystem. A stock Anaconda install creates two subvolumes: <code>root<\/code> (mounted at <code>\/<\/code>) and <code>home<\/code> (mounted at <code>\/home<\/code>). On that default, <code>\/var<\/code> lives inside the <code>root<\/code> 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Tested <strong>May 2026<\/strong> on Fedora 44 (kernel 7.0.9-202.fc44, snapper 0.13.0, grub-btrfs 4.13.1, btrfs-progs 6.19.1). Package availability and CLI parity verified on Fedora 43 (snapper 0.11.0) and Fedora 42 (snapper 0.11.0); the snapper CLI subset used in this article is stable across both branches.<\/em><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"800\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-btrfs-subvolume-fedora-44.png\" alt=\"findmnt btrfs filesystem show and subvolume list on Fedora 44\" class=\"wp-image-167755\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-btrfs-subvolume-fedora-44.png 920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-btrfs-subvolume-fedora-44-300x261.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-btrfs-subvolume-fedora-44-768x668.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Verify the Btrfs layout on Fedora<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>findmnt \/\nsudo btrfs filesystem show\nsudo btrfs subvolume list \/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>findmnt<\/code> output should show <code>btrfs<\/code> with a <code>subvol=\/root<\/code> mount option. The subvolume list will include at least <code>root<\/code> and <code>home<\/code>. Some install paths add more: custom partitioning and the Atomic\/ostree spins (Silverblue, Kinoite) carve out separate <code>var<\/code> and <code>boot<\/code> subvolumes, and systemd auto-creates <code>var\/lib\/machines<\/code>, so your exact list may have more entries than a stock Workstation install (which shows just <code>root<\/code> and <code>home<\/code>). Either way Snapper targets the <code>root<\/code> 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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install Snapper and create a config for \/<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Snapper is in the official Fedora repo. The companion snapper-tools package adds the dnf integration we use later.<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo dnf install -y snapper snapper-tools inotify-tools<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create a Snapper configuration named <code>root<\/code> for the root subvolume:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo snapper -c root create-config \/\nsudo snapper list-configs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>create-config<\/code> generates <code>\/etc\/snapper\/configs\/root<\/code>, mounts a hidden <code>.snapshots<\/code> subvolume under <code>\/<\/code>, and starts the snapper-timeline and snapper-cleanup timers. From this point on, hourly timeline snapshots accumulate automatically.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create pre- and post-install snapshots around a dnf transaction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo snapper -c root create --description \"pre-flatpak-install\"\nsudo dnf install -y cowsay\nsudo snapper -c root create --description \"post-cowsay-install\"\nsudo snapper -c root list<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The command output is shown above.<\/p>\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"800\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-create-fedora-44.png\" alt=\"snapper create-config and create snapshots before and after dnf install on Fedora 44\" class=\"wp-image-167756\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-create-fedora-44.png 920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-create-fedora-44-300x261.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-create-fedora-44-768x668.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">See what changed between two snapshots<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Two commands you will use often:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo snapper -c root status 1..2\nsudo snapper -c root diff 1..2 \/usr\/bin\/cowsay<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The command output is shown above.<\/p>\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"800\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-status-fedora-44.png\" alt=\"snapper status diff and undochange comparing snapshots on Fedora 44\" class=\"wp-image-167757\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-status-fedora-44.png 920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-status-fedora-44-300x261.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-status-fedora-44-768x668.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The first column of <code>status<\/code> output is a five-character flag string. <code>+<\/code> means &#8220;new file in the newer snapshot&#8221;, <code>-<\/code> &#8220;deleted&#8221;, <code>c<\/code> &#8220;content changed&#8221;, <code>p<\/code> &#8220;permissions changed&#8221;, <code>g<\/code> &#8220;gid changed&#8221;, <code>u<\/code> &#8220;uid changed&#8221;. For a typical dnf install you will see hundreds of <code>+<\/code> entries; for a quick config change just one or two <code>c<\/code> lines.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Selective rollback for a single file (without rebooting):<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo snapper -c root undochange 1..2 \/etc\/myconfig.conf<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This restores <code>myconfig.conf<\/code> from snapshot 1 onto the live filesystem and leaves everything else untouched. Use it for &#8220;I edited a file, broke the daemon, and want just that file back&#8221; scenarios; reach for full rollback (next section) for &#8220;the whole system needs to go back to before this dnf transaction&#8221;.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install grub-btrfs for boot-menu rollback<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">grub-btrfs hooks into Snapper and writes a &#8220;Fedora Linux snapshots&#8221; submenu into your GRUB config. From there you can boot directly into any snapshot and roll back from inside that snapshot.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">grub-btrfs is not in Fedora&#8217;s main repo. Enable the well-maintained COPR by kylegospo and install:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo dnf copr enable -y kylegospo\/grub-btrfs\nsudo dnf install -y grub-btrfs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run the snapshot detector once manually to confirm it sees your Snapper snapshots, then regenerate the GRUB config:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo \/etc\/grub.d\/41_snapshots-btrfs\nsudo grub2-mkconfig -o \/boot\/grub2\/grub.cfg<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The command output is shown above.<\/p>\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"800\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-grub-btrfs-detect-fedora-44.png\" alt=\"grub-btrfs detecting snapper snapshots and adding GRUB menu entries on Fedora 44\" class=\"wp-image-167758\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-grub-btrfs-detect-fedora-44.png 920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-grub-btrfs-detect-fedora-44-300x261.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-grub-btrfs-detect-fedora-44-768x668.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To make grub-btrfs update the menu automatically every time Snapper creates a new snapshot, enable the path unit:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo systemctl enable --now grub-btrfs.path<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On Fedora the path unit expects a <code>.snapshots.mount<\/code> dependency. If you get a &#8220;Unit \\x2esnapshots.mount not found&#8221; message, create the mount unit manually with <code>sudo systemd-mount \/var\/lib\/snapshots<\/code> or skip the path unit and run <code>grub2-mkconfig<\/code> by hand or via a dnf hook after each snapshot. The COPR ships a working hook that handles this for most users.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Boot from a snapshot and roll back the system<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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 <strong>Fedora Linux snapshots<\/strong> submenu with one entry per Snapper snapshot.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pick a snapshot. The system boots into a read-only snapshot mount; that read-only mode is the safety feature that lets you verify &#8220;yes, this snapshot does have the working state I expected&#8221; before committing to the rollback. To make the rollback permanent (the live root subvolume becomes the snapshot&#8217;s contents):<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo btrfs subvolume get-default \/\nsudo snapper -c root rollback 1\nsudo systemctl reboot<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The command output is shown above.<\/p>\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"800\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-rollback-fedora-44.png\" alt=\"snapper rollback to a snapshot reboot on Fedora 44\" class=\"wp-image-167759\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-rollback-fedora-44.png 920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-rollback-fedora-44-300x261.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-snapper-rollback-fedora-44-768x668.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><code>snapper rollback N<\/code> 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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Automatic timeline snapshots and cleanup<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Snapper&#8217;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 <code>\/etc\/snapper\/configs\/root<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>TIMELINE_CREATE=\"yes\"\nTIMELINE_CLEANUP=\"yes\"\nTIMELINE_MIN_AGE=\"1800\"\nTIMELINE_LIMIT_HOURLY=\"10\"\nTIMELINE_LIMIT_DAILY=\"10\"\nTIMELINE_LIMIT_WEEKLY=\"0\"\nTIMELINE_LIMIT_MONTHLY=\"10\"\nTIMELINE_LIMIT_YEARLY=\"10\"\n\nNUMBER_LIMIT=\"50-100\"\nNUMBER_LIMIT_IMPORTANT=\"10-20\"\nSPACE_LIMIT=\"0.5\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>SPACE_LIMIT=\"0.5\"<\/code> means &#8220;snapshots may use up to 50 percent of the filesystem&#8221;. On a 40 GB root that is plenty. After editing the config, restart the cleanup timer so it picks up the new policy:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo systemctl restart snapper-cleanup.timer\nsudo systemctl list-timers | grep snapper<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Auto-snapshot every dnf transaction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">snapper-tools ships a small dnf plugin that creates pre\/post snapshot pairs automatically around every transaction, mirroring what zypper does on openSUSE. Enable it:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo dnf install -y python3-dnf-plugin-snapper\nsudo dnf list --installed | grep snapper\nsudo dnf install -y nano\nsudo snapper -c root list | tail -5<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After the install you should see two new snapshots labeled &#8220;pre&#8221; and &#8220;post&#8221; with the dnf command line as the description. Every future <code>dnf install<\/code>, <code>dnf upgrade<\/code>, and <code>dnf remove<\/code> creates the same pair automatically. Roll back any single transaction with <code>snapper rollback &lt;post-snapshot-id&gt;<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What about \/home, \/var, and \/boot<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Fedora keeps <code>\/home<\/code> on its own Btrfs subvolume, so a root rollback never touches your downloads, browser history, or dotfiles. <code>\/var<\/code> is the one to watch. On a stock Workstation install it lives inside the <code>root<\/code> subvolume, which means a <code>snapper rollback<\/code> of <code>\/<\/code> also reverts <code>\/var<\/code>, including systemd journals, container images, and database storage under <code>\/var\/lib<\/code>. Confirm which layout you have with <code>sudo btrfs subvolume list \/<\/code>. If you want snapshots of <code>\/home<\/code> too, create a separate Snapper config:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo snapper -c home create-config \/home\nsudo snapper -c home create --description \"baseline\"\nsudo snapper list-configs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If your install does have <code>\/var<\/code> 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 <code>\/var<\/code> is part of <code>root<\/code> (the Workstation default), every root rollback rewinds it too, so the safe habit is to snapshot <code>\/<\/code> right before a risky change instead of restoring a day-old snapshot that would also throw away today&#8217;s container images and database writes. The recommended pattern is: snapshot <code>\/<\/code> aggressively before risky operations, snapshot <code>\/home<\/code> daily for safety, and know which side of that <code>\/var<\/code> split your system is on before you roll back.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Where this fits in the Fedora 44 Workstation series<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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 <a href=\"https:\/\/computingforgeeks.com\/post-install-fedora-44-workstation\/\">Fedora 44 post-install guide<\/a> 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 <a href=\"https:\/\/computingforgeeks.com\/btrfs-snapshots-snapper-automatic-rollback-fedora-opensuse\/\">Btrfs Snapshots with Snapper<\/a> guide stays the reference.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 &#8230; <a title=\"Setup Btrfs Snapshots with Snapper on Fedora 44 \/ 43 \/ 42\" class=\"read-more\" href=\"https:\/\/computingforgeeks.com\/btrfs-snapper-grub-btrfs-fedora\/\" aria-label=\"Read more about Setup Btrfs Snapshots with Snapper on Fedora 44 \/ 43 \/ 42\">Read more<\/a><\/p>\n","protected":false},"author":17,"featured_media":167741,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29,299,47,50],"tags":[208,681,39860,39859,39861],"cfg_series":[39847],"class_list":["post-167774","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-fedora","category-how-to","category-linux","category-linux-tutorials","tag-btrfs","tag-fedora","tag-grub-btrfs","tag-snapper","tag-system-rollback","cfg_series-fedora-44-workstation"],"_links":{"self":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/167774","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\/17"}],"replies":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/comments?post=167774"}],"version-history":[{"count":5,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/167774\/revisions"}],"predecessor-version":[{"id":168338,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/167774\/revisions\/168338"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media\/167741"}],"wp:attachment":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media?parent=167774"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/categories?post=167774"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/tags?post=167774"},{"taxonomy":"cfg_series","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/cfg_series?post=167774"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}