cache22-backup
cache22-backup has two engines, chosen by the root filesystem. Both write a
single archive stream that can be compressed and encrypted, and both read that
stream back from a file, stdin, or a URL.
backup captures the user-adjusted layer of an install and replays it onto a
fresh install of the same image. It does not store what the image already ships,
so the archive is small and stays valid as the base image moves. It works on any
root filesystem. On btrfs it captures each /var subvolume efficiently: nested
data subvolumes (for example incus images and VM disks) with btrfs send, so
sparse and reflinked data is not expanded into the archive; separately-mounted or
partially-excluded subvolumes (for example /var/home) with a dedicated
per-subvolume tar that restores in place. ext4/xfs roots have no subvolumes and
are captured purely as files.
clone captures every subvolume of a btrfs root, including the OS itself, using
btrfs send. Reflinks, compression, xattrs, nodatacow, and the read-only state
of each subvolume are carried natively, and shared extents (snapshots, container
and VM images) are sent once. A clone is restored onto a fresh disk by the
installer, which partitions, sets up LUKS, receives the subvolumes, rewrites the
disk-bound boot files, and re-signs the boot image with the clone’s own key. Use
clone for an exact whole-system copy; use backup for a small layer that
replays onto any matching install.
Everything is captured at the file and subvolume level. There is no
application-specific handling: container and VM state lives under /var and is
captured like any other data.
Scope is the filesystem the root lives on. Anything mounted from a different filesystem (external disk, USB, NAS, and virtual mounts such as tmpfs or fuse) is detected by its filesystem UUID and left out, while every subvolume of the root filesystem is kept.
Synopsis
sudo cache22-backup backup [options] [-o DIR|-]
sudo cache22-backup clone [options] [-o DIR|-]
sudo cache22-backup restore [options] [-i FILE|-]
cache22-backup info [options] [-i FILE|-]
The archive is a single stream (a tar, by default zstd-compressed, optionally
encrypted). With no -o, backup and clone write to a tool-owned directory; -o -
streams to stdout, so it pipes over ssh. Restore and info read a file, stdin
(-i -), or an http(s)/ftp URL. A clone is not restored with restore; it
is restored by the installer (see Restoring a clone).
Backup options
| Flag | Effect |
|---|---|
-o, --output DEST |
Where to write. Omit for the default tool-owned directory /var/lib/cache22/backup/archives (on the root filesystem, excluded from backups). - streams to stdout, for sending the archive off-box. A directory is accepted only if new, empty, or an existing cache22 repo; a non-empty multi-purpose directory is refused, so one is never tagged. Archives are auto-named host + UTC timestamp + level. |
--exclude DIR |
Exclude a directory subtree (repeatable). A btrfs subvolume at or under it is dropped entirely, so it is neither sent nor tarred. A subvolume with an exclude inside it falls back to a dedicated tar that honors the exclude. |
--include DIR |
Re-add a path excluded by default (repeatable). |
--full |
Full backup; reset the incremental state. Default when no prior state exists. |
--incremental |
Capture only /var changes since the last backup. |
--no-compress |
Store uncompressed. The default is zstd. |
--tmpdir DIR |
Staging directory. Default /var/tmp. |
--dry-run |
Report what would be captured. Write nothing. |
Clone options
| Flag | Effect |
|---|---|
-o, --output DEST |
As for backup. Archives are auto-named cache22-clone-host-time. |
--exclude DIR |
Drop a subtree or a whole subvolume (repeatable). A subvolume whose path falls under an exclude is not sent; an excluded subtree inside the OS root’s /var is removed from the sent copy. |
--include DIR |
Re-add a path excluded by default (repeatable). |
--no-compress |
Store uncompressed. The default is zstd. |
--tmpdir DIR |
Staging directory. It holds the send streams before they are packaged, so for a large system point it at a disk with room for a full copy of the data. |
--dry-run |
List the subvolumes that would be sent. Write nothing. |
A clone keeps the per-machine Secure Boot key (/var/lib/cache22/sbkey), because
the restore reuses it to re-sign the boot image. A tar backup drops it, because a
tar restore lands on an install that already has its own key.
Restore options
| Flag | Effect |
|---|---|
-i, --input FILE |
Read the archive from FILE, stdin (-, the default), or a URL. |
--force |
Restore even if the archive’s image digest differs from the running image. |
--dry-run |
Report what would be restored. Change nothing. |
Encryption
Any command accepts a key source. Encryption uses openssl enc
(AES-256-CTR, PBKDF2). An encrypted archive carries the Salted__ header, so
info, restore, and the installer detect it and ask for the key.
| Flag | Effect |
|---|---|
--passphrase |
Prompt for a passphrase. |
--passphrase-file FILE |
Read the passphrase from FILE. |
--key-file FILE |
Use FILE as a symmetric key. |
Examples
Backup (tar overlay, any filesystem)
# Full backup to the default location (/var/lib/cache22/backup/archives)
sudo cache22-backup backup
# Backup to a USB drive (a new or empty directory)
sudo cache22-backup backup -o /run/media/usb/cache22-backups
# Incremental backup: only /var changes since the last run
sudo cache22-backup backup --incremental
Clone (whole btrfs filesystem, btrfs only)
# Clone to a USB drive, encrypted (prompts for a passphrase)
sudo cache22-backup clone --passphrase -o /run/media/usb/cache22-clones
# Clone leaving large rebuildable container storage out
sudo cache22-backup clone --exclude /var/lib/containers/storage -o /mnt/disk
Over ssh (no archive ever touches local disk)
-o - writes the archive to stdout and -i - reads it from stdin, so either
engine streams over a pipe. The data is compressed (and, with a key, encrypted)
on the source, so only the compressed stream crosses the network.
# PULL a backup to the workstation (tar)
ssh root@host 'cache22-backup backup -o -' > host.c22b
# PULL an encrypted clone to the workstation
ssh root@host 'cache22-backup clone --passphrase-file /root/k -o -' > host.clone.c22b
# PUSH a tar backup from the workstation onto a freshly installed host
ssh root@host 'cache22-backup restore -i -' < host.c22b
# Restore a tar backup straight from an http server
sudo cache22-backup restore -i https://example.com/host.c22b
Inspect and restore locally
# Inspect any archive (tar or clone) without restoring
cache22-backup info -i host.c22b
# Restore a tar backup onto a fresh install of the same image
sudo cache22-backup restore -i host.c22b
A clone is restored by the installer, not by restore; see
Restoring a clone below.
Incremental backups
--incremental (tar engine only) uses a GNU tar snapshot kept in
/var/lib/cache22/backup. The first run, or --full, writes a level-0 archive
and resets the snapshot. Later --incremental runs capture only /var files
changed since the previous run. /etc is small and is always captured in full.
To restore an incremental chain, restore the full archive first, then each
increment in order; each one applies its /var delta, including deletions.
Restoring a tar backup
A tar restore lands the user layer on a freshly installed cache22 of the same
image. It does not restore disk identity (fstab, crypttab, machine-id) or
TPM enrollment, which the installer owns for the target. After a restore:
- Re-enroll TPM auto-unlock with
cache22-encryption enroll <luks-device>. - Review enabled services, then reboot.
restore refuses to run when the archive’s image digest does not match the
running image, unless --force is given. Restoring onto a different image
version is best-effort: the /etc overlay may collide where the image changed a
default the user also changed.
Restoring a clone
A clone is restored by the installer, not by restore. Boot the installer and
either give the source on the command line or enter it at the first prompt:
# From a clone on a mounted USB drive
cache22-install --restore /run/media/usb/cache22-clones/host.clone.c22b \
--disk /dev/sda --luks
# From an http server
cache22-install --restore https://example.com/host.clone.c22b --disk /dev/sda
# PUSH a clone in over ssh: stream it from the workstation into the installer
# (the target boots the installer environment, reachable over ssh)
cache22-install --restore - --disk /dev/sda < host.clone.c22b # run on the target
# e.g. driven from the workstation:
ssh root@installer-env 'cache22-install --restore - --disk /dev/sda' < host.clone.c22b
The source can be a file path, an http(s)/ftp URL, or - for stdin (so a
clone can be streamed in over ssh). For an encrypted clone, add the matching
--restore-passphrase, --restore-passphrase-file FILE, or
--restore-key-file FILE. The installer:
- Partitions the disk and, with
--luks, sets up a fresh LUKS volume with a new passphrase. Partition sizes and UUIDs do not need to match the source. - Creates a fresh btrfs and receives every subvolume from the clone, including the OS root, at its original path with its original read-only state.
- Rewrites only the disk-bound boot files for the new disk:
fstab,crypttab, the kernel command line, and theroot=UUID in the boot entries. - Re-signs the boot image with the clone’s own Secure Boot key, which rode in with the root subvolume, so the restored system boots under the same key.
The account, hostname, locale, timezone, services, and data all come from the
clone. Because the LUKS header is new, re-enroll TPM auto-unlock after first boot
with cache22-encryption enroll <luks-device>. The disk must be at least as
large as the data in the clone.
Configuration
/etc/cache22/backup.conf is sourced if present. It may add to the default
exclude set:
EXTRA_EXCLUDE=(/var/lib/some-large-cache /var/games)
The default exclude set is intentionally minimal. For a tar backup it drops
ephemeral data (/var/cache, /var/tmp), this tool’s own archive directory, and
machine-bound boot and security state (machine-id, fstab, crypttab, and the
Secure Boot key). A clone keeps the Secure Boot key but otherwise drops the same
ephemeral and self-referential paths. Everything else, including containers and
their images and VM disks, is captured. To leave large rebuildable container or
VM storage out of a backup, add it with --exclude or EXTRA_EXCLUDE, for
example /var/lib/containers/storage.
Where archives go, and why backups never capture backups
By default archives are written to the tool-owned directory
/var/lib/cache22/backup/archives, which is already excluded. A directory given
with -o is accepted only when it is new, empty, or already a cache22 backup
repo; a non-empty multi-purpose directory is refused, so the tool never tags one
by mistake. Any directory it does write into is marked with a standard
CACHEDIR.TAG and skipped by later backups (and by any tool honoring the tag),
and the current output file is excluded explicitly. Archives placed somewhere by
other means (copied in by hand) are not tagged; keep those off the backed-up
filesystem or add their directory to EXTRA_EXCLUDE.