Borg vs Restic: Painless Encrypted Backups You'll Actually Run

Because the best backup is the one that happens

Everyone agrees backups are important, and almost nobody does them properly. The reason is rarely ignorance; it is friction. A backup scheme that is fiddly, slow or expensive simply does not get run, and an un-run backup is worth precisely nothing the day the disk dies. The good news is that two excellent open-source tools — BorgBackup and Restic — have made encrypted, deduplicated, automatable backups genuinely painless. This article walks through both, so you can pick one and actually use it.

Before touching tooling, internalise the rule that has saved more data than any single product: 3-2-1. Keep 3 copies of your data, on 2 different types of media, with 1 copy off-site. The production copy counts as one. A local backup on a different disk is your second. An off-site copy — a remote server, an object store, a friend’s NAS — is the third, and it is the one that survives fire, theft and ransomware. Both Borg and Restic are designed to make that off-site copy cheap and safe, because everything is encrypted before it leaves your machine.

Both are command-line backup programs that share three crucial properties.

  • Deduplication. Files are split into chunks, and identical chunks are stored only once. Back up a 10 GB directory daily and the repository grows by the size of what actually changed, not 10 GB a day.
  • Compression. Chunks are compressed before storage, shrinking the repository further.
  • Encryption. Data is encrypted client-side with a passphrase or key. The server holding your backups never sees plaintext.

The result is space-efficient, secure backups where each “snapshot” looks like a full backup but costs roughly the size of an incremental one. The difference between the two tools is mostly in where they like to store data, which we will get to.

Install Borg from your distribution (sudo apt install borgbackup or sudo dnf install borgbackup). Borg works against a repository, which can be a local path or an SSH target.

1. Initialise the repository with encryption:

export BORG_REPO=/mnt/backup/borg-repo
borg init --encryption=repokey-blake2 "$BORG_REPO"

repokey stores the encryption key inside the repository itself, protected by your passphrase — convenient, but back the passphrase up somewhere safe. For an SSH target, the repo URL looks like ssh://user@host:22/./backups/borg-repo.

2. Create a snapshot. Borg calls them archives; naming them with a timestamp keeps things tidy:

borg create --stats --compression zstd \
  "$BORG_REPO::{hostname}-{now:%Y-%m-%d}" \
  /etc /home /var/www \
  --exclude '/home/*/.cache'

The --stats flag prints how much was deduplicated, which is satisfying the first time you see it.

3. Prune old archives so the repository does not grow forever:

borg prune --list "$BORG_REPO" \
  --keep-daily=7 --keep-weekly=4 --keep-monthly=6

This retention policy keeps a week of dailies, a month of weeklies and half a year of monthlies, expiring the rest. In recent Borg, follow prune with borg compact "$BORG_REPO" to actually free the space.

4. Check integrity periodically so you discover corruption before you need a restore:

borg check --verify-data "$BORG_REPO"

Restoring is borg extract "$BORG_REPO::archive-name", or borg mount to browse an archive as a normal directory and copy out individual files.

Install Restic (sudo apt install restic or sudo dnf install restic). Restic’s headline feature is the sheer number of backends it speaks: local, SFTP, S3-compatible object stores, Backblaze B2, Azure, Google Cloud, and more, all built in.

1. Initialise, here against an S3-compatible bucket:

export RESTIC_REPOSITORY="s3:https://s3.eu-central-1.amazonaws.com/my-backup-bucket"
export RESTIC_PASSWORD="a-long-strong-passphrase"
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
restic init

The same RESTIC_REPOSITORY of /mnt/backup/restic would target a local disk, or sftp:user@host:/srv/restic an SSH server — the rest of the commands are identical regardless of backend.

2. Back up:

restic backup /etc /home /var/www \
  --exclude="/home/*/.cache" \
  --tag nightly

Restic deduplicates against everything already in the repo, so subsequent runs are fast and small.

3. Forget and prune with a retention policy, in one go:

restic forget --tag nightly \
  --keep-daily=7 --keep-weekly=4 --keep-monthly=6 \
  --prune

forget removes the snapshot references; --prune reclaims the now-unreferenced data. Doing both together is the common pattern.

4. Restore a snapshot. List snapshots first, then restore by ID:

restic snapshots
restic restore latest --target /tmp/restore-test

You can also restic mount /mnt/restic and browse every snapshot as a filesystem.

They overlap heavily, so the decision comes down to a few honest trade-offs.

Choose Borg if storage efficiency is paramount and your backup target is a single server or local disk reachable over SSH. Borg’s deduplication and compression are excellent, and its append-only mode is a strong defence against a compromised client trying to delete history. The catch: Borg essentially wants SSH or a local path. There is no native object-store support, so cloud storage means a helper such as Rclone or Borgbase.

Choose Restic if you want to back up straight to object storage — S3, B2, Wasabi and the like — without an intermediary, or if you value backend flexibility and a single static binary. Restic is wonderfully portable and its multi-backend design is its biggest strength. The trade-off is that for some workloads it has historically used more memory and been a touch less storage-efficient than Borg, though both improve constantly.

In practice: SSH-reachable server, lean towards Borg. Cloud object store as the off-site leg, lean towards Restic. Either one, run consistently, beats the cleverest scheme you never automate.

A backup you run by hand is a backup you will forget. Wrap your chosen command in a small script, then drive it with a systemd timer. Create /etc/systemd/system/backup.service:

[Unit]
Description=Nightly Restic backup

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/backup.env
ExecStart=/usr/local/bin/run-backup.sh

And /etc/systemd/system/backup.timer:

[Unit]
Description=Run backup nightly

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=900

[Install]
WantedBy=timers.target

Persistent=true runs a missed backup if the machine was asleep at 02:30, and RandomizedDelaySec spreads load if many machines back up to the same target. Enable it with sudo systemctl enable --now backup.timer, and every run lands in the journal (journalctl -u backup.service). Keep credentials and passphrases in the EnvironmentFile, locked down to mode 600 and owned by root.

A backup you have never restored is a hypothesis, not a backup. Schedule a recurring restore test — even monthly — that pulls a known file out of the latest snapshot and checks it. For Restic:

restic restore latest --target /tmp/restore-check --include /etc/hostname
diff /etc/hostname /tmp/restore-check/etc/hostname && echo "restore OK"

For Borg, borg extract a small archive into a scratch directory and compare. Also run borg check or restic check on a schedule to catch silent corruption early. The day you genuinely need a restore is the worst possible time to discover the process does not work.

Borg and Restic both deliver encrypted, deduplicated, compressed backups that are cheap to store and quick to run. Borg edges ahead on storage efficiency over SSH; Restic wins on backend flexibility, especially straight-to-cloud. Pick the one that fits your storage, automate it with a systemd timer, satisfy the 3-2-1 rule with an off-site copy, and — above all — test your restores. The best backup remains the one that actually happens.