Skip to content

Restic Backup & Restore

Overview

Restic is an incremental, encrypted, deduplicated snapshot tool that stores repositories on S3-compatible backends. Muppy orchestrates restic through fabric tasks executed asynchronously on hosts via the IMQ message queue.

A restic repository is addressed as <bucket>/<prefix>. Its durable coordinates (S3 bucket + password vault) survive host deletion, so backups remain restorable even after the origin host is decommissioned.

Minimum restic version

Muppy requires restic 0.17.0+. The ensure_restic task installs or upgrades restic on the target host when it is missing or below this version.

Muppy objects

Object Role
Restic Repository (mpy.restic_repo) Durable coordinates (bucket + vault + prefix), retention grid, state, raw-data stats. Survives host deletion.
Restic Snapshot (mpy.restic_snapshot) Synced from restic. Stores the full 64-char snapshot id, a computed 8-char short id, backup path, tags, and size.
Vault (mpy.vault, credential type envvar) Holds RESTIC_PASSWORD. Operator-created; the password is never auto-generated. Losing it makes the repo unrecoverable.
S3 Bucket (mpy.aws_s3_bucket) S3 endpoint + credentials. Supports AWS, Cloudflare R2, OVH, and other S3-compatible backends.
Host link (mpy.host.restic_repo_id) 1:1 link from a host to a repo. The first backup auto-initializes the repo.

Repository states

State Meaning
configured Coordinates set; not yet initialized on a host.
initialized restic init has run; snapshots may exist.
detached Origin host deleted; the repo survives for restore/forget.

Configure

  1. Create or reuse an S3 Bucket (Muppy → Storages → S3 Objects).
  2. Create an envvar vault holding the restic password:
    RESTIC_PASSWORD=<your-secret>
    
    The vault's credential type must be envvar — the repo field filters on it.
  3. Create a Restic Repository: select the bucket, the vault, and a prefix.
  4. Open the target host and set its Restic Repository field (restic_repo_id).
  5. The first backup automatically runs restic init and freezes the prefix (<prefix>/<host.id>). The origin host name is also frozen at this point.

Backup

Launch from a host form or the backup wizard:

  • Folder — absolute path on the host to back up (required).
  • Tags — comma-separated labels (e.g. nightly,filestore), split into one --tag flag each.

The backup is incremental. After it completes, Muppy applies the repo retention grid with restic forget (no --prune) to drop expired snapshots, then records the new snapshot.

restic backup <folder> --tag <tag>... --host <origin_host_name> --json

Restore

Launch from a snapshot's Restore button (opens a wizard):

  • Target Host — any host that can read the repo's bucket.
  • Target Path — defaults to /var/tmp/restore_<short_id> (safe staging). Set / for an in-place restore.
  • Include Path — auto-filled from the snapshot's backup_path; scopes both the restore and --delete to that subtree.
  • Exact replica (--delete) — when checked, removes files not in the snapshot. Restic 0.17+ refuses --delete without an --include filter, so the include path is required.
restic restore <short_id> --target <path> [--include <path>] [--delete]

In-place restore is destructive

To restore an Odoo filestore in place: STOP ODOO FIRST, set target_path='/', check --delete, and keep the include path (defaults to the snapshot backup path) so restic scopes the deletion to that subtree. The restore task does not orchestrate systemd.

Sync

Reconciles Muppy snapshot records with the actual restic repository (restic snapshots --json):

  • Creates records for snapshots restic knows but Muppy doesn't.
  • Updates known records (normalizes legacy short ids to full 64-char ids).
  • Drops Muppy records whose snapshot restic no longer knows (out-of-band forget) — without raw-deleting S3 objects (restic manages its own packs).

Use Sync after an out-of-band restic forget, or to import snapshots created outside Muppy.

Prune

Applies the repo retention grid and repacks unused data:

restic forget --group-by host,paths --keep-last N --keep-daily N ... --prune --json
  • Takes an exclusive lock — no backups can run during prune.
  • Refreshes raw_data_size_bytes and last_prune_ts via restic stats.

keep_last=0 = forget all

Setting keep_last=0 (with the other keep_* at 0) tells Muppy to forget all snapshots. Restic refuses --keep-last 0 as an empty policy, so Muppy uses --unsafe-allow-remove-all --host <origin_host_name> (restic 0.17+) instead. Use this to empty a repo before destroy.

Repo lifecycle (host deletion → destroy)

When the origin host is deleted, the repo enters the detached state: it survives with its snapshots intact, and origin_host_name is preserved (frozen at init) so forget --group-by host,paths stays coherent.

  1. Detach — delete the origin host. The repo flips to state=detached.
  2. Set Execution Host — on the detached repo form, pick any host with bucket access. This persists across refreshes.
  3. Unlock — run restic unlock --remove-all (detached ⇒ all locks are guaranteed orphans since the origin host is gone).
  4. Forget all — set keep_last=0Prune → forgets every snapshot for the origin host.
  5. Sync — drops the now-orphaned Muppy snapshot records.
  6. Destroy (scan) — runs restic snapshots --json and blocks if any snapshots remain. When the repo is empty, it reports destroy_ok: True.
  7. Delete the repo record — S3 cleanup of the (now empty) prefix is a separate manual step (aws s3 rm --recursive).

Button reference

Button Restic command When to use
Prune forget --keep-* ... --prune Periodic retention enforcement + repack (exclusive lock).
Sync snapshots --json → reconcile After out-of-band forget, or to pick up external backups.
Unlock unlock [--remove-all] After a crashed backup left stale locks.
Destroy (scan) snapshots --json (scan only) Pre-deletion safety check; blocks if snapshots remain.