February 2, 2026
Time for some early spring cleaning.
I've got a shoddy backup system in place that needs to be revamped. Dropbox (which can't be fully downloaded onto my laptop, its so large) and five external hard drives, each a snapshot of the Dropbox drive at a different point over the last 10 years and with some other random things thrown onto them.
I used to have Backblaze but quit it years ago for some reason I don't remember.
I'd like to follow the 3-2-1 Backup Rule:
I actually have all this covered but not in a consistent fashion – none of the 3 copies are the same or all up-to-date. My off-site drive is in Seattle and hasn't been updated for at least 3 years.
If I could fit my Dropbox onto my laptop then I would maybe do the hacky things and periodically create a whole copy to two empty external hard drives, recycling the drives for each update. But I can't anymore.
I want a tool that helps me make backups with deduplication, so I can add to an existing backup rather than make a whole new copy. I don't want to have to hop around between backups because my 2022 photos are in one backup and my 2023 photos are in another.
I'm not doing anything too fancy nor are the stakes super high.
There are three things I want to be able to do easily:
Maintain a backup of my data that I can browse occasionally.
Backup my recent documents and photos on a monthly basis.
Browse and tag my photos.
I have Dropbox and iPhoto both backing up my recent photos from my phone. And any recent documents on my laptop that could be lost between monthly backups is probably in my email or another cloud service, nor is it super important if I didn't already put it in Dropbox, we're basically talking my Downloads folder and the random screenshots I like to take of good or bad UX on the web that I never actually look at again.
I'm testing out three open source command line tools to see which one I like best.
I don't trust any company fully with my data and it sounds like the open source tools do a solid job, so why not go with free?
I had ChatGPT come up with a very basic testing script so I could get a feel for each tool. Basically, it's a directory with a couple files that I backup, make some changes, backup again, and restore from the original backup. Making some changes allows me to see how the tool handles deduping.
Restic Documentation — restic 0.18.1 documentation
Language: Go
1brew install restic 2 3export RESTIC_REPOSITORY=/backup-test #doesn't have to be the same dir 4export RESTIC_PASSWORD=testpassword 5 6restic init 7 8restic backup src1 9 10# only the changes will be backed up 11rm src1/b.txt 12echo "file D" > src1/d.txt 13 14restic backup src1 15 16repository 3de9effd opened (version 2, compression level auto) 17created new cache in /Users/boo/Library/Caches/restic 18no parent snapshot found, will read all files 19[0:00] 0 index files loaded 20 21Files: 3 new, 0 changed, 0 unmodified 22Dirs: 1 new, 0 changed, 0 unmodified 23Added to the repository: 1.805 KiB (1.113 KiB stored) 24 25processed 3 files, 21 B in 0:00 26snapshot c6277153 saved 27 28# copy the current backup to another dir and add a file to test deduping 29cp -r src1 src2 30echo "file E" > src2/e.txt 31 32restic backup src2 33 34repository 3de9effd opened (version 2, compression level auto) 35using parent snapshot c6277153 36[0:00] 100.00% 1 / 1 index files loaded 37 38Files: 1 new, 0 changed, 2 unmodified 39Dirs: 0 new, 1 changed, 0 unmodified 40Added to the repository: 1.791 KiB (970 B stored) 41 42processed 3 files, 21 B in 0:00 43snapshot 3d20c1b2 saved 44 45# list snapshots 46restic snapshots 47 48repository 3de9effd opened (version 2, compression level auto) 49ID Time Host Tags Paths Size 50--------------------------------------------------------------------------------------- 51c6277153 2026-01-30 16:53:12 Nickis-Laptop.local /Users/boo/dev/backup-test/src1 21 B 523d20c1b2 2026-01-30 16:59:52 Nickis-Laptop.local /Users/boo/dev/backup-test/src1 21 B 533a9d7c2d 2026-01-30 17:00:05 Nickis-Laptop.local /Users/boo/dev/backup-test/src2 28 B 54d55cba5e 2026-01-30 17:00:30 Nickis-Laptop.local /Users/boo/dev/backup-test/src3 12 B 55--------------------------------------------------------------------------------------- 564 snapshots 57 58# restore a snapshot 59restic restore --target /tmp/restore-work {snapshot ID} 60 61repository 3de9effd opened (version 2, compression level auto) 62[0:00] 100.00% 4 / 4 index files loaded 63restoring snapshot c6277153 of [/Users/boo/dev/backup-test/src1] at 2026-01-30 16:53:12.927071 +0100 CET by boo@Nickis-Laptop.local to /tmp/restore-work 64Summary: Restored 4 files/dirs (21 B) in 0:00 65 66ls /tmp/restore-work 67 68rm -rf /tmp/restore-work
I like the restic commands the best of the three I've tried, mainly because I don't have to name the backup.
Language: Python
borg uses the term "archive" where restic uses "snapshot".
1export BORG_REPO=/Users/boo/dev/backup-test-2/repo #needs to be an empty dir 2export BORG_PASSPHRASE=testpassword 3 4borg init --encryption=repokey 5 6IMPORTANT: you will need both KEY AND PASSPHRASE to access this repo! 7 8Key storage location depends on the mode: 9- repokey modes: key is stored in the repository directory. 10- keyfile modes: key is stored in the home directory of this user. 11 12For any mode, you should: 131. Export the borg key and store the result at a safe place: 14borg key export REPOSITORY encrypted-key-backup 15borg key export --paper REPOSITORY encrypted-key-backup.txt 16borg key export --qr-html REPOSITORY encrypted-key-backup.html 172. Write down the borg key passphrase and store it at safe place. 18 19borg create $BORG_REPO::path/to/src1 src1 20 21borg list $BORG_REPO 22src1 Sat, 2026-01-31 22:06:03 [e1bb98882f01e225e2a099aa83611fb91870ca5e5383d8391478c7b834e711ad] 23 24rm src1/c.txt 25echo "file D" > src1/d.txt 26 27borg create $BORG_REPO::src1-2 src1 28borg create $BORG_REPO::src2 src2 29 30borg list $BORG_REPO 31src1 Sat, 2026-01-31 22:06:03 [e1bb98882f01e225e2a099aa83611fb91870ca5e5383d8391478c7b834e711ad] 32src1-2 Sat, 2026-01-31 22:10:25 [32b95f572f19ed6d103a184789c6aa060324789fa3b87570de66356dcb0ce0c1] 33src2 Sat, 2026-01-31 22:12:42 [8a9fe564ab649e47736e1a01e81e116946bf74ca131e6e1b94b14b1ad2dea520] 34 35borg info 36Repository ID: be5ca57233423b0ad2cc12b2a262b20790d1dde7b9a4f0c34a93740f234c39e7 37Location: /Users/boo/dev/backup-test-2/repo 38Encrypted: Yes (repokey) 39Cache: /Users/boo/.cache/borg/be5ca57233423b0ad2cc12b2a262b20790d1dde7b9a4f0c34a93740f234c39e7 40Security dir: /Users/boo/.config/borg/security/be5ca57233423b0ad2cc12b2a262b20790d1dde7b9a4f0c34a93740f234c39e7 41------------------------------------------------------------------------------ 42 Original size Compressed size Deduplicated size 43All archives: 42 B 300 B 2.87 kB 44 45 Unique chunks Total chunks 46Chunk index: 10 12 47 48borg extract $BORG_REPO::src1-2
The requirement to manually name each snapshot/archive could be either a pro or a con. The naming scheme I've used in the example above is terrible. I'm not sure what I'd want to use other than a date, which is repeated in the list.
As you can see from the init process, a key is created. You need both the key and the passphrase to access a borg repository.
Interesting features to explore:
Language: Go
1kopia repository create filesystem --path /Users/boo/dev/backup-test-3/repo 2Enter password to create new repository: testpassword 3Re-enter password for verification: testpassword 4Initializing repository with: 5block hash: BLAKE2B-256-128 6encryption: AES256-GCM-HMAC-SHA256 7key derivation: scrypt-65536-8-1 8splitter: DYNAMIC-4M-BUZHASH 9Connected to repository. 10 11NOTICE: Kopia will check for updates on GitHub every 7 days, starting 24 hours after first use. 12To disable this behavior, set environment variable KOPIA_CHECK_FOR_UPDATES=false 13Alternatively you can remove the file "/Users/boo/Library/Application Support/kopia/repository.config.update-info.json". 14 15Retention: 16Annual snapshots: 3 (defined for this target) 17Monthly snapshots: 24 (defined for this target) 18Weekly snapshots: 4 (defined for this target) 19Daily snapshots: 7 (defined for this target) 20Hourly snapshots: 48 (defined for this target) 21Latest snapshots: 10 (defined for this target) 22Ignore identical snapshots: false (defined for this target) 23Compression disabled. 24 25To find more information about default policy run 'kopia policy get'. 26To change the policy use 'kopia policy set' command. 27 28NOTE: Kopia will perform quick maintenance of the repository automatically every 1h0m0s 29and full maintenance every 24h0m0s when running as boo@nickis-Laptop. 30 31See https://kopia.io/docs/advanced/maintenance/ for more information. 32 33NOTE: To validate that your provider is compatible with Kopia, please run: 34 35$ kopia repository validate-provider 36 37kopia snapshot create /Users/boo/dev/backup-test-3/backup_dir 38Snapshotting boo@nickis-Laptop:/Users/boo/dev/backup-test-3/backup_dir ... 39* 0 hashing, 3 hashed (21 B), 0 cached (0 B), uploaded 211 B, estimating... 40Created snapshot with root k8631e4a74fa1bfb7327d82b503a46a8b and ID 1f6bf45929328b1372b1d8b37237e294 in 0s 41Running full maintenance... 42GC found 0 unused contents (0 B) 43GC found 0 unused contents that are too recent to delete (0 B) 44GC found 7 in-use contents (1.1 KB) 45GC found 2 in-use system-contents (1.2 KB) 46GC undeleted 0 contents (0 B) 47Compacting an eligible uncompacted epoch... 48Advancing epoch markers... 49Attempting to compact a range of epoch indexes ... 50Cleaning up unneeded epoch markers... 51Cleaning up old index blobs which have already been compacted... 52Cleaned up 0 logs. 53Finished full maintenance. 54 55kopia snapshot list /Users/boo/dev/backup-test-3/backup_dir 56boo@nickis-Laptop:/Users/boo/dev/backup-test-3/backup_dir 572026-02-01 10:33:07 CET k8631e4a74fa1bfb7327d82b503a46a8b 21 B drwxr-xr-x files:3 dirs:4 (latest-1..2,hourly-1,daily-1,weekly-1,monthly-1,annual-1) 58+ 1 identical snapshots until 2026-02-01 10:33:58 CET 59 60kopia ls -l k8631e4a74fa1bfb7327d82b503a46a8b 61drwxr-xr-x 21 2026-01-31 22:14:47 CET k6647c300e69cecccd21f920519c29abb src1/ 62drwxr-xr-x 0 2026-01-31 22:14:40 CET k1f7f41f99d34ce2bda1e9b82182cec5e src2/ 63drwxr-xr-x 0 2026-01-31 22:14:40 CET kf1038421ff24d338a963ef2deef7bc44 src3/ 64 65kopia show k6647c300e69cecccd21f920519c29abb 66{"stream":"kopia:directory","entries":[{"name":"a.txt","type":"f","mode":"0644","size":7,"mtime":"2026-01-31T21:14:47.012334056Z","uid":501,"gid":20,"obj":"694bbedb98415f944e757fb04da85a00"},{"name":"b.txt","type":"f","mode":"0644","size":7,"mtime":"2026-01-31T21:14:47.012475682Z","uid":501,"gid":20,"obj":"680c1e63c73751bf535d6f56c1904958"},{"name":"c.txt","type":"f","mode":"0644","size":7,"mtime":"2026-01-31T21:14:47.012585432Z","uid":501,"gid":20,"obj":"cf894e34efbbdea5902193cd762aa89f"}],"summary":{"size":21,"files":3,"symlinks":0,"dirs":1,"maxTime":"2026-01-31T21:14:47.012585432Z","numFailed":0}}
I'd recommend creating env variables for kopia because it requires the use of absolute paths.
Interesting features to explore:
After just a quick glance, kopia is exciting because it appears to have some great features for more complex use cases. There appears to be multiple contributors and an active user community. I like, for example, that there is a discourse forum (but unfortunately also a Slack). Complexity, on the other hand, often means more interdependencies and therefore higher risks.
I'm going to move forward with borg mainly because my partner also uses it (aka in-house help desk).
Note: Here's why syncing a backup repo isn't recommended: Can I copy or synchronize my repo to another location?