Reviewed 30 August 2026 against the existing backup.sh, the previous research, current upstream documentation, and tagged source trees.
Restic is not the best choice if minimizing tools and code is the primary requirement and you have an independent SSH backup server. In that case, keep rsync and build snapshots from ordinary directories with --link-dest. This adds only a small shell wrapper and makes recovery possible with cp or rsync.
Restic remains the best practical compromise if Mega must remain the destination and you want encrypted, space-efficient snapshots, safe retention, browsing, and integrity checks. Rsync cannot use Mega, while rclone alone is transport rather than a complete backup engine. Restic is operationally compact—a single static executable—but its implementation is not suckless-small.
My preferred order is therefore:
Keep rsync for server synchronization regardless. Synchronization and backup are different jobs.
The previous report optimized for the current Mega backend and a complete backup feature set. Under those assumptions, restic was correctly the best fit. It did not give enough weight to the user’s stronger requirement: minimize added programs, source code, opaque formats, and operational concepts.
Restic is minimal at deployment time, not in implementation:
go.mod (34 direct, 59 marked indirect), with a much larger transitive module graph;
Those features are useful and cohesive for a backup repository, but this is not a small, easily audited Unix tool. The static binary hides runtime dependency management; it does not remove the compiled dependency and audit surface.
Run the backup from the backup server, pulling data over SSH. Create each snapshot in a staging directory and publish it only after rsync succeeds:
/backups/workstation/
2026-08-29T010000Z/
2026-08-30T010000Z/
latest -> 2026-08-30T010000Z
.staging-1234/
The essential transfer is:
rsync -aHAX --numeric-ids --delete --delay-updates \
--link-dest=/backups/workstation/PREVIOUS \
workstation:/source/ /backups/workstation/.staging-1234/
After success, rename the staging directory to its timestamp and atomically replace latest. Never update an already-published snapshot, and never combine this design with --inplace: rsync warns that in-place updates can modify hard-linked files and damage snapshot history.
rsync -aHAXn for a dry run and rsync -aHAX to restore.
The official rsync site itself publishes a rotating seven-day incremental-backup example, and --link-dest is the supported mechanism for hard-linking unchanged destination files.
These are not small footnotes: they are the functionality removed by choosing fewer tools.
A pull backup is preferable: workstation → read-only SSH/rsync → backup server.
The backup server owns the SSH key and pulls from a restricted read-only source account. The workstation has no credential that can erase old snapshots. OpenSSH’s restrict and forced-command options, together with rsync’s rrsync -ro, can constrain that account.
A push design gives a compromised workstation credentials to its backup destination. Restic encryption does not prevent a holder of write/delete credentials from deleting the repository; restic’s own threat model says this explicitly. Mega account history or a second offline copy is still necessary.
Neither rsync nor restic makes a transactionally consistent copy of a live SQLite file. SQLite explicitly warns that a background file copy can combine old and new content. Preserve the existing use of SQLite’s online .backup, VACUUM INTO, or the newer sqlite3_rsync utility. Validate representative restores with PRAGMA integrity_check.
Rootless container volumes still require podman unshare, and application data may need quiescing. A backup program cannot manufacture application-level consistency.
These figures are orientation, not a quality ranking. They count physical lines—including comments and blanks—in tracked implementation files, while excluding tests, documentation, examples, generated/vendor trees, and build output. Multi-repository libraries are included where they form part of the product. Exact comparisons across C, Go, Python/Cython, Rust, Perl, and shell are inherently imperfect.
| Candidate | Version/commit | Approx. implementation lines | Deployment/fit observation |
|---|---|---|---|
| No new engine: local wrapper | project-specific | likely well under 300 | Reuses rsync/SSH; ordinary files; policy remains ours |
| rsync-time-backup | 9b3ea2d |
638 | Tiny, but current SSH construction disables host-key verification and uses eval; use as reference, not unchanged |
| rsnapshot | e7d7820 |
7,861 main Perl program | Mature rsync wrapper; more rotation/config machinery than needed |
| bup | 0.33.10 | 20,262 | Lean content chunking, but no integrated repository encryption and upstream admits important maturity gaps |
| Borg | 1.4.5 | 43,359 | Narrow local/SSH focus; authenticated encryption, chunk dedup, checks; standalone binary around 30 MB |
| restic | 0.19.1 | 48,675 | Broad backends and complete repository model; static binary around 31 MB |
| rustic CLI + core/backend libraries | 0.11.4 family | about 37,000 | Smaller own CLI, but default features include TUI, WebDAV, Prometheus, OpenTelemetry, and jq; 757 packages appear in its lockfile |
| Kopia | 0.23.1 | over 110,000 | Strong but clearly too broad here; rclone support is documented as experimental |
| rclone | 1.75.0 | over 330,000 | Already needed for Mega, but very broad; not a snapshot engine |
The tagged trees used for direct measurements were cloned shallowly. A representative command was:
git ls-files '*.go' |
grep -vE '(^|/)(testdata|tests?)/|_test\.go$' |
xargs wc -l
Equivalent implementation suffixes and exclusions were used for each language. Rustic’s total includes the separately versioned rustic_core and rustic_backend libraries used by the CLI; counting only the CLI repository would misleadingly report about 11,927 lines.
A crucial perspective: modern rsync itself is not tiny (roughly 115,000 implementation lines by a similar broad measurement). Its advantage here is zero marginal tool count, an established operational role, a transparent output format, and a narrower backup wrapper—not that rsync’s protocol implementation is small.
Choose for Mega. It has an officially documented rclone backend and starts rclone itself. It supplies exactly the capabilities missing from the current dated-folder script: encrypted/authenticated objects, content-defined chunk deduplication, complete snapshots, browse/diff/restore, retention, locking, interruption-safe repository ordering, and check.
Its repository is intentionally opaque without restic and the password. It also needs forget, occasional prune, checks, and restore drills. It cannot protect a repository from deletion by an attacker with storage credentials.
Choose when an SSH backup host exists and full repository features are worth one new tool. Borg’s scope is narrower than restic’s: local and SSH repositories rather than many clouds. It provides chunk deduplication, compression, authenticated encryption, archive mounting/extraction, and integrity checks. Efficient remote operation normally installs Borg on both ends, unlike rsync which is already present in the user’s workflow and restic’s SFTP backend which needs only SSH/SFTP remotely.
Do not replace restic with rustic merely to chase a lower line count. Rustic implements the restic format and supports rclone/SFTP, but the current default build includes a TUI, WebDAV server, Prometheus, OpenTelemetry, and jq functionality. It is a faster-moving, less mature choice and conflicts with the stated wish to avoid breadth. A deliberately feature-minimized custom build is possible, but maintaining a custom build profile adds complexity rather than removing it.
Bup is the most interesting lean chunk-deduplicating option and works over SSH. However, it has no integrated encrypted repository, depends on Python/C/Git tooling, describes itself as less tested than tar and as missing important features, and has immature metadata caveats. It is not the right primary private off-site backup.
Rsnapshot is reasonable if a maintained, configurable rsync rotation wrapper is preferred over a local script. Its nearly 8,000-line Perl program and interval semantics buy little for this specific source list.
Rsync-time-backup is only 638 lines and produces the desired transparent snapshots. Its current script, however, constructs remote commands with eval and passes StrictHostKeyChecking=no plus UserKnownHostsFile=/dev/null. That is a poor default for a backup trust boundary. A smaller purpose-built wrapper using normal argument boundaries and normal SSH host verification is easier to audit.
Rclone should remain storage plumbing, not the backup model. It has no content deduplication, snapshot catalogue, or retention graph. The current dated-directory scheme duplicates trees and owns unsafe retention logic. Mega also lacks modification times and hashes, allows duplicate names, and may temporarily block repeated rclone logins. Restic’s long-lived rclone service and internal hashes reduce some issues, but do not make Mega an ideal repository backend.
Replace the Mega-primary design with a backup-server pull using rsync --link-dest. Keep the wrapper deliberately small and explicit:
backups.sh create
backups.sh list
backups.sh restore SNAPSHOT PATH TARGET
backups.sh check
backups.sh prune
list can be directory listing, restore can call rsync, check can validate protected SHA-256 manifests, and prune can implement one simple retention policy. Do not add configuration frameworks or generic backend abstraction.
Before choosing it, measure one week of changed data. If frequently modified large files make hard-link snapshots too large, that is concrete evidence to use Borg/restic chunk deduplication rather than a hypothetical feature argument.
Keep the previous restic + rclone recommendation. Do not switch to rustic, Kopia, Duplicity, or a home-grown encrypted chunk store. Restic is larger than the philosophy prefers, but replacing its cryptography, interruption safety, indexes, retention, and verification with shell code would be false minimalism: fewer installed binaries but a larger, riskier system owned locally.
Pilot it before migration, retain the current backups during overlap, and prove selective and full restores. If Mega behaves badly, change storage—not the restic repository model—to native SFTP or S3-compatible object storage.
Use rsync snapshots when you control a backup server; use restic when you must turn Mega/rclone into a real encrypted, deduplicated backup repository.
http://prabhashs-g10:8787/backup-redesign-research/
--link-dest, --inplace, transfer verification, filters): https://rsync.samba.org/ftp/rsync/rsync.1.html