Skip to content
All articles

Linux Server Backups with restic and BorgBackup: A Practical Guide

restic and BorgBackup give Linux servers encrypted, deduplicated backups. This guide covers repositories, database dumps, append-only protection against ransomware, retention, scheduling, restore tests and monitoring.

M By Max · 6 min read
Linux Server Backups with restic and BorgBackup: A Practical Guide

For file-level backups of Linux servers, restic and BorgBackup are the two proven open-source choices. Both encrypt data on the server before it leaves, deduplicate so daily backups stay small, and keep many restore points. A good setup has five parts: an encrypted repository on a different machine or provider, database dumps taken right before each backup, a retention policy applied with forget/prune, a systemd timer that runs it unattended, and monitoring plus regular restore tests. Ideally, the server being backed up cannot delete its own backups.

This guide applies to current Debian 12/13, Ubuntu 24.04 and RHEL/AlmaLinux 9/10 servers. If you run virtual machines on Proxmox, image-level backups are covered in our Proxmox Backup Server guide; file-level backups complement them.

restic or Borg?

Both are mature and well documented. The practical differences:

restic BorgBackup
Storage targets Local, SFTP, REST server, S3-compatible, Azure, Google Cloud Storage, rclone Local or SSH (Borg must be installed on the remote side)
Encryption Always on (AES-256 with Poly1305 authentication) Chosen at borg init; repokey with a strong passphrase is the documented default choice
Protection from a compromised client rest-server --append-only borg serve --append-only via SSH forced command
Packaging Single Go binary; in Debian, Ubuntu and EPEL In Debian, Ubuntu and EPEL; this guide uses the stable 1.4 series

Pick restic when you want object storage (S3-compatible buckets) or one binary everywhere. Pick Borg when you back up to your own SSH server and like its flexible compression options. Do not run both on the same data; pick one and do it well.

Whichever you choose, store the repository password or key somewhere other than the server, such as your password manager. Both projects are explicit: lose the password (and for Borg, the key) and the backups are unreadable. For Borg, export the key with borg key export and store it safely.

Step 1: Dump databases first

Copying the live files of a running PostgreSQL or MySQL/MariaDB database does not give you a consistent backup. Dump them first, then back up the dump.

## PostgreSQL: custom format, restorable with pg_restore
sudo -u postgres pg_dump -Fc appdb > /var/backups/db/appdb.dump

## MySQL / MariaDB (InnoDB): consistent dump without locking tables
mysqldump --single-transaction --routines --triggers appdb > /var/backups/db/appdb.sql
## On MariaDB the same tool is also called mariadb-dump

restic can also stream a dump directly into the repository, without a temporary file:

restic backup --stdin-filename appdb.sql \
  --stdin-from-command -- mysqldump --single-transaction appdb

restic checks the command's exit code: if the dump fails, no snapshot is created and restic exits with an error, which is exactly what you want.

Step 2: Create the repository

Keep the repository off the server: another server in a different location, a backup appliance, or an S3-compatible bucket. A repository on the same disk protects against nothing but mistakes.

restic

Put the settings in a root-only environment file, /etc/restic/env (mode 600):

RESTIC_REPOSITORY=sftp:[email protected]:/srv/restic/web01
RESTIC_PASSWORD_FILE=/etc/restic/password

For S3-compatible storage, use these lines instead:

RESTIC_REPOSITORY=s3:https://s3.example-provider.com/company-backups/web01
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
sudo bash -c 'set -a; . /etc/restic/env; restic init'

SFTP requires key-based SSH login to the backup host, because automated backups cannot answer a password prompt.

Borg

export BORG_PASSCOMMAND="cat /etc/borg/passphrase"
borg init --encryption=repokey [email protected]:/srv/borg/web01
borg key export [email protected]:/srv/borg/web01 /root/web01-borg-key.txt
## Move that key file to your password manager, then delete it from the server

The Borg documentation recommends repokey with a strong passphrase as the general choice, and repokey-blake2 if the CPU lacks hardware-accelerated SHA-256.

Step 3: Make backups append-only

Ransomware and intruders increasingly go after backups. If the server can delete its own repository, an attacker who owns the server can too. Both tools support an append-only mode where the client can add backups but not delete or modify existing ones:

  • restic: run the repository behind rest-server with --append-only (and --private-repos so each server sees only its own repository).
  • Borg: on the backup server, restrict the client's SSH key in ~/.ssh/authorized_keys:
command="borg serve --append-only --restrict-to-path /srv/borg/web01",restrict ssh-ed25519 AAAA... web01-backup

The trade-off: pruning old backups must then happen from a separate, well-protected admin machine with full access, not from the client. Borg's documentation also warns that compaction does nothing in append-only mode and that a later prune in normal mode makes deletions permanent, so check the repository (borg check --verify-data) before pruning. Storage-side snapshots or versioning on the backup host, which the client cannot change, add another layer. This is the "immutable copy" idea behind the 3-2-1 backup strategy.

Server hardware in a rack used as a backup target

Step 4: Run backups with a systemd timer

Systemd timers are easier to monitor than cron jobs: every run is logged in the journal, and Persistent=true catches up on a run missed while the server was off.

/etc/systemd/system/restic-backup.service:

[Unit]
Description=restic backup
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/env
ExecStartPre=/usr/local/sbin/dump-databases.sh
ExecStart=/usr/bin/restic backup --exclude-file=/etc/restic/excludes /etc /home /srv /var/backups/db
Nice=10
IOSchedulingClass=idle

/etc/systemd/system/restic-backup.timer:

[Unit]
Description=Nightly restic backup

[Timer]
OnCalendar=*-*-* 01:30:00
RandomizedDelaySec=20m
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer

The Borg equivalent calls borg create with an archive name such as '{hostname}-{now}' and --compression lz4, followed by borg prune and borg compact (when the repository is not append-only).

Step 5: Retention with forget and prune

Decide how far back you need to go, then let the tool enforce it. A common policy is 7 daily, 4 weekly and 12 monthly restore points.

## restic: preview first, then apply
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --dry-run
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune

## Borg
borg prune --glob-archives '{hostname}-*' --keep-daily 7 --keep-weekly 4 --keep-monthly 12
borg compact

Two details from the official documentation matter here. In restic, forget only removes snapshot references; data is actually deleted by prune (or forget --prune), and pruning locks the repository, so schedule it away from backups. In Borg, the --glob-archives '{hostname}-*' filter keeps one server's prune from touching another server's archives in a shared repository.

Step 6: Check integrity and test restores

A backup you have never restored is a hope, not a backup.

## restic: verify structure, plus read a sample of the actual data
restic check --read-data-subset=5%

## restore one directory to a scratch location
restic restore latest --target /tmp/restore-test --include /etc

## Borg
borg check [email protected]:/srv/borg/web01
borg extract --dry-run [email protected]:/srv/borg/web01::web01-2026-10-27T01:30:00

Once a quarter, do a real restore drill: restore a database dump into a test server, start the application and confirm the data is current. Write down how long it took; that is your realistic recovery time.

Step 7: Monitor failures

Most backup failures are silent: an expired cloud credential, a full disk, a changed password. Watch for them:

  • Exit codes. restic exits 0 on success, 1 on a fatal error (no snapshot) and 3 when some files could not be read (incomplete snapshot). Treat 3 as a warning to investigate, not as success.
  • systemd status. systemctl status restic-backup.service and journalctl -u restic-backup.service show the last run. An OnFailure= unit can send an alert.
  • A "dead man's switch". Have the job report success to your monitoring system and alert when that report does not arrive, which also catches a timer that never ran.
  • Snapshot age. restic snapshots --latest 1 or borg list --last 1 should always show last night's date.

Backup checklist

  • Repository on another machine or provider, encrypted, password stored off the server.
  • Database dumps before every file backup.
  • Append-only access for the client; pruning from a separate admin machine.
  • Systemd timer with Persistent=true and failure alerts.
  • Retention enforced with forget --prune or prune + compact.
  • Monthly integrity checks and quarterly restore drills.

Want your server backups verified?

Core Florida IT designs and monitors backups for business servers across Miami-Dade and South Florida, with remote support nationwide, on cloud and dedicated servers and managed IT support plans. If you are not sure your last backup would actually restore, request a free IT assessment and we will test it with you.

Sources

Keep reading

Let's make your IT the strongest part of your business.

Book a free, no-obligation IT assessment. We'll show you exactly where you stand and what to fix first.

Call Free assessment