Getting snapper/btrfs-assistant to work with dnf5

I’m a novice user (not a developer) and am looking ahead to the Fedora 41 release coming up. I have been using the btrfs-assistant python3-dnf-plugin-snapper setup to provide a really basic, yet helpful snapshot/rollback feature, which automatically takes pre/post snapshots whenever I use dnf to install/remove or upgrade.

I realize that this would not work ootb with dnf5, so I’ve been lurking and seeing if there will be a solution. Thanks to various users here (and on Reddit) I think the following is a solution.

I just want to make it public, all in one post, so folks can comment on it.

Here’s how to have a simple snapper auto-snapshot setup in Fedora 41 with dnf5:

First install:

sudo dnf install btrfs-assistant python3-dnf-plugin-snapper

Snapper settings in Btrfs Assistant (these meet my needs; your settings may be different):

Create new config “root”, Backup path: /

  • Disable timeline snapshots
  • Snapper timeline disabled
  • Snapper cleanup enabled
  • Snapper boot disabled
  • Apply systemd changes
  • Save

Next is the stuff necessary for Fedora 41/dnf5:

sudo dnf install libdnf5-plugin-actions

then create a new file:

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

Add this to the file (This came from here and Reddit. Not sure which is the chicken or egg)

# Get snapshot description
pre_transaction::::/usr/bin/sh -c echo\ "tmp.cmd=$(ps\ -o\ command\ --no-headers\ -p\ '${pid}')"
# Creates pre snapshot before the transaction and stores the snapshot number in the "tmp.snapper_pre_number"  variable.
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre_number=$(snapper\ create\ -t\ pre\ -p\ -d\ '${tmp.cmd}')"

# If the variable "tmp.snapper_pre_number" exists, it creates post snapshot after the transaction and removes the variable "tmp.snapper_pre_number".
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre_number}"\ ]\ &&\ snapper\ create\ -t\ post\ --pre-number\ "${tmp.snapper_pre_number}"\ -d\ "${tmp.cmd}"\ ;\ echo\ tmp.snapper_pre_number\ ;\ echo\ tmp.cmd

Write/Exit file

So far I’ve tested in a VM and it works as expected. I hope this is helpful to see it all in one post.

I posted the file on reddit too because i wanted people to know about it but didn’t care enough to create a new thread, cheers

Added btrfs-assistant, dnf5, snapper

updated the config to include a cleanup algo due to a suggestion on the github issue for this problem

# /etc/dnf/libdnf5-plugins/actions.d/snapper.actions
# Get snapshot description
pre_transaction::::/usr/bin/sh -c echo\ "tmp.cmd=$(ps\ -o\ command\ --no-headers\ -p\ '${pid}')"
# Creates pre snapshot before the transaction and stores the snapshot number in the "tmp.snapper_pre_number"  variable.
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre_number=$(snapper\ create\ -c\ number\ -t\ pre\ -p\ -d\ '${tmp.cmd}')"

# If the variable "tmp.snapper_pre_number" exists, it creates post snapshot after the transaction and removes the variable "tmp.snapper_pre_number".
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre_number}"\ ]\ &&\ snapper\ create\ -c\ number\ -t\ post\ --pre-number\ "${tmp.snapper_pre_number}"\ -d\ "${tmp.cmd}"\ ;\ echo\ tmp.snapper_pre_number\ ;\ echo\ tmp.cmd

1 Like

Here is an updated version of script to backup home too:

# Get the snapshot description
pre_transaction::::/usr/bin/sh -c echo\ "tmp.cmd=$(ps\ -o\ command\ --no-headers\ -p\ '${pid}')"

# Creates pre snapshots for root and home and stores snapshot numbers in variables
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre_root=$(snapper\ -c\ root\ create\ -t\ pre\ -p\ -d\ '${tmp.cmd}')"
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre_home=$(snapper\ -c\ home\ create\ -t\ pre\ -p\ -d\ '${tmp.cmd}')"

# Creates post snapshots for root and home if pre snapshot numbers exist
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre_root}"\ ]\ &&\ snapper\ -c\ root\ create\ -t\ post\ --pre-number\ "${tmp.snapper_pre_root}"\ -d\ "${tmp.cmd}"
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre_home}"\ ]\ &&\ snapper\ -c\ home\ create\ -t\ post\ --pre-number\ "${tmp.snapper_pre_home}"\ -d\ "${tmp.cmd}"

I’ll try to tidy up to make sure I understand and give new users the chance to have everything in one set of steps. To simplify, I’ll show a configuration with only snapper (without btrfs-assistant) and without any changes to the standard configuration with which this package is preconfigured (who wants to change something install btrfs-assistant). I’ll also insert the buckup script for both home and root into the file, since these are the two default subvolumes in classic Fedora installations.
I would be grateful :pray:t2: if you could give me feedback on the correctness of everything.

  1. Let’s install snapper:
    sudo dnf install snapper

  2. Let’s tell snapper which subvolumes to snapshot:
    sudo snapper -c root create-config /
    and
    sudo snapper -c home create-config /home

  3. Let’s install the other necessary packages:
    sudo dnf install python3-dnf-plugin-snapper libdnf5-plugin-actions

  4. Let’s create/edit the file where we want to insert the script:
    sudo nano /etc/dnf/libdnf5-plugins/actions.d/snapper.actions

and insert the following content inside:

# Get the snapshot description
pre_transaction::::/usr/bin/sh -c echo\ "tmp.cmd=$(ps\ -o\ command\ --no-headers\ -p\ '${pid}')"

# Creates pre snapshots for root and home and stores snapshot numbers in variables
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre_root=$(snapper\ -c\ root\ create\ -t\ pre\ -p\ -d\ '${tmp.cmd}')"
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre_home=$(snapper\ -c\ home\ create\ -t\ pre\ -p\ -d\ '${tmp.cmd}')"

# Creates post snapshots for root and home if pre snapshot numbers exist
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre_root}"\ ]\ &&\ snapper\ -c\ root\ create\ -t\ post\ --pre-number\ "${tmp.snapper_pre_root}"\ -d\ "${tmp.cmd}"
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre_home}"\ ]\ &&\ snapper\ -c\ home\ create\ -t\ post\ --pre-number\ "${tmp.snapper_pre_home}"\ -d\ "${tmp.cmd}"
  1. Save and exit.

I think it should also be specified that this configuration will not work for all updates and installations that occur via GNOME Software because it uses PackageKit (which does not work with snapper); it should also be specified that any use of snapper to boot the system from a specific snapshot (in case the system has problems booting) would require the grub-btrfs package and additional configurations; however, this package is not present in the Fedora repositories at the moment.

I hope everything is correct; I await your feedback.

The problem with Timeshift is that to use btrfs you need to have the partitions with the same names as they would have in the default partitioning in Ubuntu.

Yep you are totally right. I removed that part of the topic as to not confused folks in this thread :slight_smile:

I do have one question though. Is python3-dnf-plugin-snapper still necessary even with the custom action script for ibdnf5-plugin-actions?

In this guide he isn’t using the python3-dnf-plugin-snapper. So I’m just curious myself. Your steps are DEFINITELY easier to follow though. According to him that isn’t necessary anymore since Fedora 41 uses dnf5 as default and that plugin isn’t available or necessary anymore.

How to Install Fedora 41 with Full Disk Encryption, Snapshot, and Rollback Support

Finally, in your last action script box, you seem to have commands duplicated twice for the pre and post actions.

I can’t give you a precise answer on this.

Two pre for root and home and two post for root and home.