Snapshot and rollback of apt-installed packages

Is there a way to take a snapshot of the packages installed with apt/apt-get on a Debian/Ubuntu system, and to roll back to that snapshot later?

My use case is the following: I'd like to write a script that will build a program with large set of build dependencies. I'd like to apt install those dependencies at the start of the script. Some of those dependencies may already be installed, in which case some of the installations will be-no ops. After building the program, I'd like to get the system back in the state it was in before the build, i.e. I'd like to uninstall the things my script installed, but otherwise leave things untouched.

In other words, I want to roll apt back to a snapshot!

I'm imagining something like:

magic-command-that-writes-apt-snapshot my-packages.txt
apt install gcc texlive fortran75 cobol60 qbasic fftw 
./configure && && make && make install
magic-command-that-rollsback-to-apt-snapshot my-packages.txt

Maybe this is possible by parsing /var/log/apt/history.log but that seems very fragile.

Some specifics that might make things simpler or more complicated:

  • I'd like a general solution that is robust against the initial state of the machine, but I can guarantee my script will contain exactly one apt install command (not several, not dist-upgrade, not build-deps).
  • In this case, I'm not worried about packages that the apt command upgrades. That's a benign side-effect for my purposes. The rollback should not uninstall them, but it's fine if it doesn't downgrade them.
  • The script needs to remain non-interactive (it happens to run in a docker build).
  • I want to avoid "collateral damage". In particular, I can't assume packageA should be uninstalled simply because because packageB was installed by the script, packageB depends on packageA, and packageA has no dependents other than packageB. Maybe packageA was already installed! In other words, packages should be uninstalled if and only if they were installed by the script. Whether they have dependents is irrelevant. (This requirement is the reason this question is not a dupe of questions like this, if I understand correctly.)

Solution 1:

Built-In Options

To my knowledge, apt/apt-get does not have a built in function that does what you're looking for. There is the Snapshot - Debian that seems to do something like that, though I don't think it fully covers your requirements.

Alternatives

These are some options to consider, as I don't see a hard requirement for using Ubunut listed.

Solus (eopkg)

While not related to Ubuntu specifically, I do know the Solus offers this with their package manager: eopokg Docs

Example

Quote from the link provided:

History

You can see the history from eopkg by using:

sudo eopkg history

Rollback

To rollback your system, first use the above history command to check what the transaction / operation number was. Then, we use the following command:

sudo eopkg history -t number

The number, in this case, is the operation before the one you want to change. So if the number was 100, then you would use 99.

Yum (Fedora/CentOS/RHEL)

Yum does have this ability built in. You can find this in their official documentation, here and here

Example

Referencing their documentation you could:

sudo yum history list all

Find the transaction you wish to undo and get more info on it.

sudo yum history info <transaction_ID>

Then undo it:

sudo yum undo <transaction_ID>

Solution 2:

You can simply try to use the 'apt-rollback' script you can found here:

https://gitlab.com/fabio.dellaria/apt-rollback

wget https://gitlab.com/fabio.dellaria/apt-rollback/-/raw/master/apt-rollback.sh && chmod +x ./apt-rollback.sh

using the following command:

./apt-rollback.sh --last
Usage: apt-rollback [--last] [--remove/--reinstall package-name] [--help]

  --last       Undo the last APT command
               Supports the undo of the only Install, Remove and Purge commands

  --remove     Remove an INSTALLED package and related configuration files
               Removing also all its first installed dependencies

  --reinstall  Reinstall a REMOVED package,
               and all its first installed dependences
               Reproducing exactly its first installation

  --help       Print the help

You can see a little video demo here: apt-rollback Video Demo

Solution 3:

Apt does not have a snapshot feature. It has generally not needed one. It does most of what you want already.

Your Ubuntu system DOES have an excellent backup feature, usually meant for data, but it can also be used for packages. It's installed by default on all *buntu desktop systems.

The use-case of installing (and uninstalling) a large, complex set of software --that may include both packages and compiled binaries-- is better handled by Snaps than by debs.

EDIT: The OP's comments have clarified the use case -- we don't know which packages are already installed, and we want the removals to match the installs. Happily, apt can do that, too. Here's are two different ways:

  1. Use apt-marking. Apt keeps track of which packages you specified to install vs which packages were pulled in as dependencies. You can change that tracking. In general, top-level packages should be apt-marked "manual", and all dependencies should be apt-marked "auto"

Install like normal: sudo apt install foo bar baz This apt-marks all three packages as "manually" installed.

  • Apt will not remove them until you specify their removal.
  • Dependencies that get pulled in like libfoo will be apt-marked "auto".
  • If bar is already installed, it WON'T get installed again, but it's apt-mark will be changed from "auto" to "manual" because a human wants it installed.

You cannot safely use apt remove foo bar baz because removing bar will also remove everything that previously depended upon bar. So here's the trick: Change the apt-marking back to "auto"

     sudo apt-mark auto foo bar baz    // Make packages eligible for autoremove  
     sudo apt autoremove               // Remove Foo and Baz (and their dependencies)
                                       // Since something else "manual" still depends upon Bar, don't remove it.
  1. Apt logs what is actually installed at /var/log/apt. Parse that log to determine your list of removals.