Backup and restore redesign research

Repository reviewed: /home/prabhash-dissanayake/Documents/dotfiles; research based on current official documentation.

Recommendation

Replace the dated Mega directory tree with one encrypted restic repository, stored on Mega through restic’s official rclone backend. Keep a small local wrapper named backups.sh with explicit subcommands:

backups.sh create
backups.sh list
backups.sh files SNAPSHOT [PATH]
backups.sh diff OLD NEW
backups.sh restore SNAPSHOT PATH TARGET
backups.sh check
backups.sh prune

The scheduled job becomes backups.sh create. This is a better fit than backup.sh: the program manages a collection of backups and both creation and recovery, while avoiding the awkward backup.sh backup interface.

This recommendation keeps the current Mega account and rclone configuration, but delegates versioning, deduplication, encryption, retention, browsing, integrity checking, and restore mechanics to a backup tool designed for them.

What is wrong with the current design

The present script is a custom snapshot store built from rclone primitives:

Two existing choices should be preserved:

Why binary data does not call for Git

Git and GitHub solve source-history and collaboration, not backup recovery. Git can identify that a binary object changed, but it cannot provide a useful semantic diff for an arbitrary SQLite database, image, browser state file, or container volume. Git LFS moves large object contents elsewhere; it does not turn them into meaningful diffs or provide backup retention and recovery testing.

A deduplicating snapshot repository is the usual solution:

  1. Each run records a point-in-time file tree.
  2. Files are split into content-defined chunks.
  3. Chunks already present are referenced rather than uploaded again.
  4. Snapshot metadata gives each version a date, host, paths, and ID.
  5. Old snapshot metadata can be expired independently of shared chunks.

This works well for binaries because changes to parts of large files generally require only new chunks. “Diff” means path-level changes and byte/storage totals. Semantic SQLite differences still require application-specific SQL queries or dumps; no generic backup tool can infer them.

Options considered

Option Strengths Problems here Verdict
Current dated rclone folders Transparent remote files; no new tool Full tree copies, custom unsafe retention, no deduplication/catalog/check/restore Replace
rclone --backup-dir versioning Small change; retains overwritten/deleted files Still directory-based, no content deduplication or snapshot catalog; recovery remains custom Too little improvement
restic through rclone Encrypted snapshots, compression, content deduplication, retention policies, diff, ls, find, FUSE mount, selective restore, integrity checks; rclone backend is officially documented Repository is intentionally opaque without restic and its password; prune/check require maintenance; Mega must be validated Best fit
Kopia through rclone Similar deduplication/encryption, rich policies, automatic maintenance, optional GUI Kopia calls rclone support experimental and lists Dropbox, OneDrive, and Google Drive as tested—not Mega Avoid for this backend
BorgBackup Mature deduplication, encryption, archives, mount and extract Designed for local storage or a host reachable over SSH; Mega is not a supported direct target Best only if storage changes to an SSH/Borg host

Restic is the smallest change in architecture: it already supports rclone:<remote>:<path>, starts rclone itself, and rclone already supports the configured Mega remote. Kopia adds policy/GUI machinery but offers weaker assurance for this exact storage path. Borg would require changing providers or adding a server/mount layer.

Proposed repository model

Use one repository path, for example:

rclone:mega:backups/restic

A single repository deduplicates across dates and sources. Preserve absolute source paths in snapshots so equal basenames cannot collide. Tag special backup sets only where operationally necessary, such as desktop, sqlite, or containers; do not recreate one repository or directory hierarchy per item.

Before each create:

  1. Acquire the existing non-overlap lock.
  2. Create consistent temporary SQLite copies.
  3. Back up the configured paths with stable exclusions and Helium selection rules.
  4. Treat every non-zero restic result as a failed/incomplete run; in particular, restic documents exit status 3 as an incomplete snapshot caused by unreadable sources.
  5. Remove temporary database copies.
  6. Expire snapshots only after a successful new snapshot, but never make retention depend solely on calendar age.

The exact handling of container volumes and selective Helium paths needs a small prototype. It should retain podman unshare and use explicit source lists rather than copying unwanted browser caches into a staging tree.

Retention

A sensible starting policy for one daily run is:

--keep-daily 7 --keep-weekly 5 --keep-monthly 12

This is grandfather-father-son thinning: detailed recent recovery points and progressively sparse older history. Restic’s --keep-daily 7 keeps the latest snapshot from the last seven days that contain snapshots, so missed runs do not erase the last good backup as the current calendar cutoff can.

Policy should be previewed with forget --dry-run. Run expiry regularly, but run remote prune less often—perhaps weekly or monthly—because prune may download and re-upload partially used packs. Final values should be chosen after measuring the first repository size and daily changed-data rate against the Mega quota.

Restore design

Restore is part of the normal interface, not an emergency afterthought.

Safe default behavior:

A quarterly restore drill should recover representative examples to a temporary directory: one note, one picture, one SQLite database, one Helium profile component, and one container volume. Compare file hashes where appropriate and open/query the restored database. A backup is not proven until recovery is exercised.

Integrity, credentials, and failure domains

Mega-specific caution

Rclone’s Mega backend has unusual limitations: no modification times or hashes, duplicate names are possible, and repeated commands may cause temporary account blocking. A restic repository does not need Mega modification times/hashes because restic verifies its own encrypted, content-addressed data, but backend behavior must still be tested.

Before migration, run a disposable repository pilot containing representative data, then perform:

  1. two backups with changed and unchanged binary files;
  2. snapshot listing and diff;
  3. selective and full restores;
  4. restic check --read-data;
  5. retention dry-run, forget, prune, and another restore;
  6. interruption/retry testing;
  7. a quota and runtime comparison with the current job.

If Mega proves unreliable under repository workloads, retain restic and change only the backend—preferably to supported S3-compatible object storage. The repository/tool decision and provider decision should remain separate.

Migration sequence

Do not convert or delete the existing tree in place.

  1. Install restic through the existing package-management convention and add the wrapper.
  2. Initialize a new repository under a new Mega path.
  3. Run the first full backup and all pilot checks/restores.
  4. Switch the scheduled job only after a successful end-to-end restore.
  5. Keep the old dated backups unchanged for at least their existing seven-day overlap, preferably longer if quota permits.
  6. Remove the old tree only after a second successful scheduled backup and documented restore drill.

Sources