Files
apt-pacman-hook/README.md
hygienic-books 0a3b71ce1b docs(docs): explain APT snapshot operation
Document installation, APT protocol handling, dataset selection, recursive
snapshots, naming fields, package shortening, retention chains, and every
runtime configuration parameter.

Explain the relationship with the existing Pre-Invoke stop-gap and make the
expected callback frequency and direct-dpkg limitation explicit. Include
examples that show how affected package names appear in snapshot names.
2026-09-16 09:05:33 +02:00

6.2 KiB

apt-pacman-hook

Ubuntu APT hook for automatic ZFS snapshots.

This is the Ubuntu/APT counterpart to zfs-pacman-hook. It snapshots selected ZFS datasets before APT invokes dpkg, includes affected Debian package names in snapshot names, and prunes old snapshots using separate ordinary and important-package retention chains.

Setup

Install the files as follows:

sudo install -Dm755 apt-zfs-snapshot.sh /usr/local/sbin/apt-zfs-snapshot
sudo install -Dm644 apt-zfs-snapshot.conf /etc/apt-zfs-snapshot.conf
sudo install -Dm644 90apt-zfs-snapshot.conf /etc/apt/apt.conf.d/90apt-zfs-snapshot.conf

Set dry_run='true' in /etc/apt-zfs-snapshot.conf, run a harmless APT operation, and inspect the planned names. Set it back to false after verifying dataset selection and naming.

The helper runs as root through APT and requires Bash, zfs, and findmnt.

APT integration

The hook uses DPkg::Pre-Install-Pkgs. APT sends its version-3 package action protocol on the file descriptor named by APT_HOOK_INFO_FD. Records contain the package name, old and new versions, version direction, architecture, and action. This captures dependencies selected by APT as well as explicitly requested packages.

Actions are classified as follows:

APT record Operation marker
No old version and a new version inst
New version greater than old version upgr
New version lower than old version down
**REMOVE** action rmvl

Configuration-only records are ignored. If one callback contains multiple operation classes, the marker is mixd. In normal Ubuntu use, an apt-get -y dist-upgrade produces one useful callback and therefore one snapshot, matching the existing DPkg::Pre-Invoke stop-gap. APT can invoke dpkg more than once for complex operations; this implementation creates one snapshot per package-action callback.

Dataset selection

By default, datasets with this exact ZFS property are selected:

zfs set space.quico:auto-snapshot=true rpool/ROOT/ubuntu

With snap_only_local_datasets='true', only mounted ZFS datasets are selected. This avoids snapshotting another operating system stored in the same pool. To explicitly select roots, set:

snapshot_roots='bpool/BOOT rpool/ROOT'

Explicit roots bypass property discovery and the mounted-dataset check. Each root is snapshotted recursively, and all roots are passed to one zfs snapshot -r command.

Snapshot names

Names use this form:

dataset@apt_DATE_COUNTER_op:OP_sev:SEVERITY_pkgs:PACKAGE-LIST

Example:

rpool/ROOT/ubuntu@apt_2026-09-16-1430_1_op:upgr_sev:imp_pkgs:linux-image-6.17:systemd:libc6

The timestamp is UTC by default and is controlled by date_format and timezone. The counter starts at one and increases if a complete name already exists. This prevents collisions when identical operations happen within the same timestamp interval.

Package names are separated with package_separator, which defaults to :. Plus signs are replaced with underscores before inclusion in a ZFS name. The package list is shortened by dropping complete names from the end first, then truncating the final remaining name and adding ... when at least four characters fit. packages_max_length is a best effort because the complete ZFS name is limited to 255 characters. If the fixed dataset and metadata fields cannot fit, the hook refuses to create a snapshot.

Retention

If any affected package matches important_names, the snapshot belongs to the important chain. Otherwise it belongs to the trivial chain. The default important expression is:

linux-image(-.*)?|linux-headers(-.*)?|systemd|zfs-(dkms|utils)

The defaults retain 25 trivial snapshots and 10 important snapshots. Retention runs only after a successful snapshot and only considers snapshots with this tool's prefix and the current severity marker. Recursive snapshot trees are destroyed with zfs destroy -r.

Configuration

/etc/apt-zfs-snapshot.conf is sourced as a Bash fragment. Available parameters are:

Parameter Default Meaning
dry_run false Print planned snapshots without changing ZFS
important_names kernel/systemd/ZFS expression Exact regex for important packages
snapshots_trivial_keep 25 Trivial snapshots retained per dataset
snapshots_important_keep 10 Important snapshots retained per dataset
trivial_suffix trv Ordinary severity marker
important_suffix imp Important severity marker
packages_max_length 30 Maximum package-list characters
snap_only_local_datasets true Restrict property-selected datasets to mounted datasets
snapshot_roots empty Explicit space-separated recursive snapshot roots
field_separator _ Separator between snapshot fields
package_separator : Separator between package names
snapshot_prefix apt Prefix identifying these snapshots
date_format %F-%H%M Format passed to date
timezone Etc/UTC Timestamp timezone
install_suffix inst Install marker
remove_suffix rmvl Remove marker
upgrade_suffix upgr Upgrade marker
downgrade_suffix down Downgrade marker
mixed_suffix mixd Multiple operation classes marker

The property name and value are fixed to space.quico:auto-snapshot=true, matching the original tool.

Existing stop-gap hook

The previous DPkg::Pre-Invoke hook has no package-list input. This project uses DPkg::Pre-Install-Pkgs to retain the same practical pre-dpkg placement while adding package-aware names. Do not run both hooks unless two snapshots per APT operation are intentional.

Limitations

  • The hook covers package changes when APT invokes dpkg; direct dpkg operations are not intercepted.
  • APT may invoke dpkg more than once in complex cases, producing more than one snapshot.
  • The snapshot occurs before the corresponding dpkg invocation, not before APT downloads packages or resolves dependencies.
  • Snapshot failures are reported but do not abort the package operation, matching the warning-oriented original behavior.

License

MIT. See LICENSE.