Take Snapper snapshots around Portage transactions, in the spirit of snap-pac for pacman.
One emerge run produces exactly one pre / post snapshot pair, regardless
of how many packages it installs or removes, so you can roll a whole update
back as a unit.
Portage has no single "before the transaction" hook, only per-package phases. portage-snapper bridges that gap:
- pre (
hook.bash, sourced from/etc/portage/bashrc): runs during each package'spreinst(about to be merged) orprerm(about to be removed) phase, so installs, upgrades, and depcleans are all covered. A marker file ties the run together: the first package finds none and opens apresnapshot, noting theemergeprocess the run belongs to; every later package finds the marker and only appends its own name to the package list. - post (
post_emerge, run from/etc/portage/bin/post_emerge): runs once after the run finishes, closing thepresnapshot with a matchingpostsnapshot described by the package list, then removes the marker.
post_emerge runs whether or not the emerge run succeeds, so even a run that
fails partway is bracketed by a complete pre/post pair — roll back to the
pre snapshot to undo it. A run that never gets that far, such as one killed
by Ctrl-C, leaves its marker behind; the next run sees that the emerge the
marker names is gone and opens a pre snapshot of its own. The marker also
lives on /run (tmpfs), so an orphan never survives a reboot either.
app-backup/snapperwith a configured Snapper config (default:root)- A snapshot-capable filesystem (btrfs)
Available from the ursm overlay:
eselect repository add ursm git https://github.com/ursm/portage-overlay.git
emaint sync -r ursm
emerge app-backup/portage-snappermake install # honors DESTDIR / PREFIX / SYSCONFDIRInstalls hook.bash, config.bash, and post_emerge under
/usr/share/portage-snapper/, and a default /etc/portage-snapper.conf.
Installation places the scripts but does not touch /etc/portage/, which is
the administrator's territory. Wire the two hooks up yourself:
-
pre — add this line to
/etc/portage/bashrc(anywhere; it coexists with your existing hooks):source /usr/share/portage-snapper/hook.bash -
post — point
/etc/portage/bin/post_emergeat it. If you have no other post-emerge logic, a symlink is enough:ln -s /usr/share/portage-snapper/post_emerge /etc/portage/bin/post_emerge
Otherwise call it from your own wrapper (it is a plain subprocess, so your own code before and after it runs normally):
#!/bin/bash /usr/share/portage-snapper/post_emerge
Set PORTAGE_SNAPPER_SKIP to a truthy value to snapshot nothing for that
emerge, the way snap-pac uses SNAP_PAC_SKIP:
PORTAGE_SNAPPER_SKIP=1 emerge -uDN @worldportage sanitizes the phase environment, so the variable would not normally
reach the hook. portage-snapper works around this by reading the invoking
emerge process's own environment (/proc/<pid>/environ), so setting it on
the command line is enough — no portage configuration required.
Edit /etc/portage-snapper.conf. Every setting has a default, so an empty file
is fine.
| Setting | Default | Description |
|---|---|---|
PORTAGE_SNAPPER_CONFIG |
root |
Snapper config to snapshot |
PORTAGE_SNAPPER_DESC_LIMIT |
72 |
max length of the post description |
PORTAGE_SNAPPER_CLEANUP |
number |
Snapper cleanup algorithm (number or timeline) |
PORTAGE_SNAPPER_MARKER |
/run/portage-snapper-current |
per-transaction marker path |