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¶
- Create or reuse an S3 Bucket (Muppy → Storages → S3 Objects).
- Create an envvar vault holding the restic password:
The vault's credential type must be
RESTIC_PASSWORD=<your-secret>envvar— the repo field filters on it. - Create a Restic Repository: select the bucket, the vault, and a prefix.
- Open the target host and set its Restic Repository field (
restic_repo_id). - The first backup automatically runs
restic initand 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--tagflag 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--deleteto that subtree. - Exact replica (--delete) — when checked, removes files not in the snapshot.
Restic 0.17+ refuses
--deletewithout an--includefilter, 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_bytesandlast_prune_tsviarestic 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.
- Detach — delete the origin host. The repo flips to
state=detached. - Set Execution Host — on the detached repo form, pick any host with bucket access. This persists across refreshes.
- Unlock — run
restic unlock --remove-all(detached ⇒ all locks are guaranteed orphans since the origin host is gone). - Forget all — set
keep_last=0→ Prune → forgets every snapshot for the origin host. - Sync — drops the now-orphaned Muppy snapshot records.
- Destroy (scan) — runs
restic snapshots --jsonand blocks if any snapshots remain. When the repo is empty, it reportsdestroy_ok: True. - 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. |