Back Up Linux with Borg or restic
Set up encrypted, deduplicated backups with BorgBackup or restic: local and remote repos, retention pruning, restoring files, and systemd timer scheduling.
Before you start
- ▸SSH key-based authentication configured if using a remote backup server
- ▸Sufficient disk space on the backup destination (estimate 1.5× source size for initial run)
- ▸Root or sudo access on the machine being backed up
- ▸Remote host has BorgBackup installed if using Borg over SSH
Backups fail silently or not at all until you need them. Borg and restic are two modern, deduplicating backup tools that encrypt data at rest, handle remote repositories efficiently, and integrate cleanly with systemd timers for scheduling. This guide covers both tools side-by-side: initializing repos locally and over SSH, running pruned backups, restoring files, and automating everything with systemd.
Choosing Between Borg and restic
Both tools deduplicate and encrypt. The key differences:
- BorgBackup: requires Borg on both client and server; faster for large sequential workloads; single-binary on most distros.
- restic: backend-agnostic (local, SFTP, S3, Backblaze B2, rclone targets); no server-side binary needed for SFTP mode; slightly simpler syntax for multi-destination use.
If your remote target is a plain SSH server you control, either works. If you need S3 or a cloud backend, use restic. Both are solid choices for day-to-day use.
Installation
BorgBackup
# Debian / Ubuntu
sudo apt install borgbackup
# Fedora / RHEL 9+ (with EPEL on RHEL)
sudo dnf install borgbackup
# Arch
sudo pacman -S borg
restic
# Debian / Ubuntu
sudo apt install restic
# Fedora / RHEL
sudo dnf install restic
# Arch
sudo pacman -S restic
After installing, run restic self-update or check your distro's version against upstream releases; packaged versions sometimes lag by several minor versions.
Initializing a Repository
Borg — Local Repo
borg init --encryption=repokey-blake2 /mnt/backup/borg-repo
You will be prompted for a passphrase. Store it somewhere safe—losing it means losing access to every archive. Export a copy of the key immediately:
borg key export /mnt/backup/borg-repo ~/borg-key.txt
Borg — Remote Repo over SSH
borg init --encryption=repokey-blake2 \
user@backuphost:/srv/borg/myhostname
Borg must be installed on the remote server. Use SSH key authentication; password prompts break unattended backups.
restic — Local Repo
restic init --repo /mnt/backup/restic-repo
restic — Remote Repo over SFTP
restic init --repo sftp:user@backuphost:/srv/restic/myhostname
restic — S3-Compatible Bucket
export AWS_ACCESS_KEY_ID=your-key
export AWS_SECRET_ACCESS_KEY=your-secret
restic init --repo s3:https://s3.amazonaws.com/your-bucket-name
Running a Backup
Borg
Archive names conventionally embed a timestamp. The :: separator divides repo path from archive name.
borg create \
--verbose \
--filter AME \
--list \
--stats \
--show-rc \
--compression lz4 \
--exclude-caches \
--exclude '/home/*/.cache' \
--exclude '/home/*/.local/share/Trash' \
/mnt/backup/borg-repo::'{hostname}-{now:%Y-%m-%dT%H:%M}' \
/home \
/etc \
/var/lib
restic
restic backup \
--repo /mnt/backup/restic-repo \
--verbose \
--exclude-caches \
--exclude '/home/*/.cache' \
--exclude '/home/*/.local/share/Trash' \
/home /etc /var/lib
Pass --password-file /root/.backup-passphrase (file mode 600) to avoid interactive prompts in automated runs.
Retention and Pruning
Without pruning, repos grow unboundedly. Both tools support keeping a defined number of snapshots per time window.
Borg — Prune
borg prune \
--list \
--glob-archives '{hostname}-*' \
--show-rc \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
/mnt/backup/borg-repo
After pruning, run borg compact /mnt/backup/borg-repo (Borg 1.2+) to actually free disk space. Prune only marks archives as deletable.
restic — Forget and Prune
restic forget \
--repo /mnt/backup/restic-repo \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--prune
The --prune flag combines forgetting and reclaiming space in one step. Omit it to do a dry-run that shows what would be removed.
Restoring Files
Borg — List and Extract
# List all archives
borg list /mnt/backup/borg-repo
# List contents of a specific archive
borg list /mnt/backup/borg-repo::myhostname-2024-06-01T02:00
# Extract a single directory to current working directory
borg extract \
/mnt/backup/borg-repo::myhostname-2024-06-01T02:00 \
home/alice/Documents
borg extract strips leading slashes; run it from the restore target or use --strip-components as needed.
restic — List and Restore
# List snapshots
restic snapshots --repo /mnt/backup/restic-repo
# Restore a full snapshot to a target directory
restic restore latest \
--repo /mnt/backup/restic-repo \
--target /mnt/restore
# Restore a specific path from a snapshot ID
restic restore 3a7f2c1b \
--repo /mnt/backup/restic-repo \
--include /home/alice/Documents \
--target /mnt/restore
Scheduling with systemd Timers
systemd timers replace cron for managed services. Create a service unit and a timer unit for each backup job. The example below uses restic; adapt paths and the ExecStart line for Borg.
Service Unit
sudo tee /etc/systemd/system/restic-backup.service <<'EOF'
[Unit]
Description=restic backup
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
EnvironmentFile=/etc/restic/env
ExecStart=/usr/bin/restic backup \
--repo ${RESTIC_REPOSITORY} \
--password-file /etc/restic/passphrase \
--exclude-caches \
--exclude '/home/*/.cache' \
/home /etc /var/lib
ExecStartPost=/usr/bin/restic forget \
--repo ${RESTIC_REPOSITORY} \
--password-file /etc/restic/passphrase \
--keep-daily 7 --keep-weekly 4 --keep-monthly 6 \
--prune
EOF
Create /etc/restic/env (mode 600) with your repository URL and any cloud credentials:
RESTIC_REPOSITORY=sftp:user@backuphost:/srv/restic/myhostname
Timer Unit
sudo tee /etc/systemd/system/restic-backup.timer <<'EOF'
[Unit]
Description=Run restic backup daily at 02:30
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
Persistent=true means if the machine was off at 02:30, the backup runs at next boot rather than being skipped.
Verification
Both tools provide integrity checks. Run these periodically—monthly at minimum—or after any hardware concern.
# Borg
borg check /mnt/backup/borg-repo
# restic
restic check --repo /mnt/backup/restic-repo
Check the timer status and last run outcome:
systemctl status restic-backup.timer
journalctl -u restic-backup.service -n 50
Troubleshooting
- Borg: "Repository already exists" — You ran
borg inittwice. List archives withborg listto confirm the repo is intact and skip init. - Borg: stale lock file — If a backup was interrupted, run
borg break-lock /path/to/repo. Only do this when no other Borg process is actively using the repo. - restic: "Fatal: unable to open config file" — The repo path is wrong or the repo was never initialized. Double-check
RESTIC_REPOSITORYin your environment file. - SSH authentication failures in timer — The service runs as root by default. Ensure root's SSH key (or the key specified via
ssh-agent/IdentityFile) is authorized on the remote host. Test withssh -i /root/.ssh/id_ed25519 user@backuphost. - Timer never fires after boot — Confirm the timer is enabled (
systemctl is-enabled restic-backup.timer) and checksystemctl list-timersfor next trigger time. - Disk space not freed after Borg prune — Run
borg compactafterborg prune. Borg 1.2 split these into separate operations.
Frequently asked questions
- Can I back up to multiple destinations simultaneously?
- restic makes this straightforward: initialize two separate repos and run two backup commands (or two service units). Borg supports multiple remotes via separate init/create calls as well, though there is no built-in multi-target flag in either tool.
- What happens if I forget the repository passphrase?
- There is no recovery path—the encryption is strong by design. Store the passphrase in a password manager and keep an offline copy. For Borg, also export and store the key file separately from the repo.
- How much extra storage does deduplication save in practice?
- For typical home directories with incremental daily changes, deduplication usually reduces cumulative storage by 60–90% compared to full copies. Repos with large, frequently changing binary files (databases, VMs) see less benefit.
- Should I exclude /proc, /sys, /dev, and /run?
- Both tools skip virtual filesystems by default when you specify real paths like /home and /etc. If you back up /, explicitly exclude /proc, /sys, /dev, /run, /tmp, and any mounted bind or overlay filesystems to avoid errors and useless data.
- Is the systemd timer approach better than cron?
- For backup jobs, yes. systemd timers log output to the journal automatically, support missed-run catchup via Persistent=true, and integrate with dependency ordering (e.g., After=network-online.target). cron has no equivalent built-in logging or catch-up behavior.
Related guides
How to Check Disk Health with SMART
Learn to use smartctl to read SMART attributes, run drive self-tests, and identify early warning signs of HDD and SSD failure before data loss occurs.
Debug systemd Units that Won't Start
Learn a repeatable workflow to debug systemd services that won't start: status output, journalctl, systemd-analyze verify, and safe override.conf patches.
Linux Server Disaster Recovery Checklist
A practical Linux server disaster recovery checklist: what to back up, RTO/RPO planning, immutable off-site copies, automated restore drills, and verification.
How to Fix a Broken GRUB Bootloader
Fix a broken GRUB bootloader by booting from live media, chrooting into your installed system, reinstalling GRUB, and regenerating grub.cfg — covers BIOS/MBR and UEFI/GPT.