2026-08-01

Restic backups done right: encryption, retention, browsing and recovery

#backup #linux #restic #security

Restic backups done right

Restic is one of the most useful backup tools available to a Linux administrator. It stores encrypted snapshots in a local or remote repository, transfers only data that is not already present, supports compression, verifies repository integrity and lets you browse or restore old versions without unpacking a chain of archive files.

This tutorial builds a complete, maintainable setup on Linux. It starts with a local repository on an external disk, then covers SFTP, exclusions, automation, retention, verification, browsing, selective recovery and a safer two-copy strategy.

The examples were reviewed with Restic 0.19.1. Always read the release notes before upgrading a production backup system.

A backup is not proven by a successful `backup` command. It is proven by a successful restore of the data you actually need.

1. What Restic does

A Restic repository contains a sequence of snapshots. Each snapshot represents the selected files and directories at a particular moment, but Restic does not create a complete second copy every time. Data is split into content-defined chunks, encrypted and deduplicated. A chunk already present in the repository does not need to be stored again.

This gives Restic several useful properties:

  • every snapshot looks complete from the user's point of view;
  • repeated backups normally store only new or changed data;
  • duplicate content can be reused even when it appears in different files;
  • repository contents are encrypted;
  • repository integrity can be checked;
  • old snapshots can be removed according to a retention policy;
  • snapshots can be mounted and browsed like a filesystem on supported systems.

Compression is supported by repository format version 2, which is the current default for newly created repositories.

2. Install Restic

On Ubuntu or Debian:

sudo apt update
sudo apt install restic
restic version

Distribution packages can lag behind upstream. For a production deployment, compare the installed version with the current stable Restic release. Official standalone binaries can also be installed, and an official binary can update itself with:

sudo restic self-update

Do not automate blind upgrades. Test a new release against a non-critical repository first and keep a copy of the previous binary until your checks and restore test have passed.

3. Choose the repository location

The repository is the destination that holds encrypted backup data and metadata. It may be:

  • a directory on another local disk;
  • a removable USB disk;
  • an SFTP server;
  • a Restic REST server;
  • Amazon S3 or compatible object storage;
  • another backend supported directly or through `rclone`.

For the first setup, assume that an external disk is mounted at:

/media/backupdisk

The repository will be:

/media/backupdisk/restic/my-workstation

Do not store the only repository on the same physical disk as the source. That protects against accidental deletion but not against disk failure, theft, electrical damage or ransomware with access to both locations.

4. Create a protected configuration directory

Create a root-owned directory for Restic configuration:

sudo install -d -m 0700 /etc/restic

Create a long, unique repository password:

sudo sh -c 'umask 077; openssl rand -base64 48 > /etc/restic/password'

Check the permissions:

sudo stat -c '%a %U:%G %n' /etc/restic/password

Expected output:

600 root:root /etc/restic/password

The password encrypts access to the repository. Losing it means losing the backup. Store a separate offline copy in a password manager or another secure location that will still be available during a disaster.

Do not put the password directly on the command line. Command-line arguments can be exposed through shell history or process inspection. A protected password file or password command is usually safer.

5. Define environment variables

Create `/etc/restic/workstation.env`:

sudo tee /etc/restic/workstation.env > /dev/null <<'EOF'
RESTIC_REPOSITORY=/media/backupdisk/restic/my-workstation
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
EOF

Protect it:

sudo chmod 0600 /etc/restic/workstation.env
sudo install -d -m 0700 /var/cache/restic

Load it into the current shell when working manually:

set -a
source /etc/restic/workstation.env
set +a

Confirm the repository path without printing the password:

printf '%s\n' "$RESTIC_REPOSITORY"

6. Initialise the repository

Make sure the external filesystem is mounted, then create the repository:

sudo mkdir -p /media/backupdisk/restic/my-workstation
sudo -E restic init

A successful initialisation creates the repository structure and encryption keys.

Run this only once for a given repository. If `restic init` reports that a repository already exists, stop and investigate instead of replacing anything.

List repository keys:

sudo -E restic key list

Restic supports multiple keys for one repository. A new key can be added before retiring an old password:

sudo -E restic key add

Never remove the last usable key.

7. Decide what to back up

For a workstation, a reasonable starting set might be:

/home
/etc
/usr/local
/opt
/root

Do not blindly back up pseudo-filesystems, caches and transient runtime data. A full Linux recovery strategy also needs package lists, database dumps, application configuration, credentials and documentation of storage layout. Restic can preserve files, but it does not automatically turn an arbitrary live server snapshot into a bootable bare-metal image.

For databases, create a consistent dump first. Backing up live database files while they are being modified may produce a snapshot that is difficult or impossible to restore correctly.

Example MariaDB dump directory:

sudo install -d -m 0700 /var/backups/database
sudo bash -o pipefail -c 'mariadb-dump --all-databases --single-transaction --routines --events \
  | gzip -c > /var/backups/database/all-databases.sql.gz'

Use the database vendor's recommended backup procedure for important systems.

8. Create an exclusion file

Create `/etc/restic/excludes.txt`:

sudo tee /etc/restic/excludes.txt > /dev/null <<'EOF'
# User caches and trash
/home/*/.cache
/home/*/.local/share/Trash

# Browser caches
/home/*/.mozilla/firefox/*/cache2
/home/*/.cache/google-chrome
/home/*/.cache/chromium

# Development dependencies that can be rebuilt
**/node_modules
**/.venv
**/vendor

# Temporary and runtime data
/tmp
/var/tmp
/var/cache
/var/run
/run

# Virtual filesystems
/proc
/sys
/dev

# Mounted filesystems are handled separately
/mnt
/media
EOF

Protect the file:

sudo chmod 0600 /etc/restic/excludes.txt

Review exclusions carefully. `node_modules` may be disposable in one project and contain irreplaceable local modifications in another. Exclude only data you are genuinely able to recreate.

9. Run the first backup

With the environment loaded:

sudo -E restic backup \
  /home \
  /etc \
  /usr/local \
  /opt \
  /root \
  /var/backups/database \
  --exclude-file=/etc/restic/excludes.txt \
  --one-file-system \
  --tag workstation \
  --tag automatic

Restic shows progress in an interactive terminal. The summary reports new, changed and unmodified files, processed data, data added to the repository and the new snapshot ID.

`--one-file-system` prevents Restic from crossing into other mounted filesystems below a selected source path. This is useful for avoiding accidental recursion into backup disks, network mounts or large unrelated volumes. Back up additional filesystems explicitly when they are required.

Run a second backup without changing anything:

sudo -E restic backup \
  /home /etc /usr/local /opt /root /var/backups/database \
  --exclude-file=/etc/restic/excludes.txt \
  --one-file-system \
  --tag workstation \
  --tag manual-test

The second run still scans metadata, but should add very little data when the sources are unchanged.

10. Inspect snapshots

List all snapshots:

sudo -E restic snapshots

Group them by host and paths:

sudo -E restic snapshots --group-by host,paths

Show the latest snapshot:

sudo -E restic snapshots --latest 1

Display repository statistics:

sudo -E restic stats

For a more detailed view of stored data:

sudo -E restic stats --mode raw-data

Snapshot size is the logical size represented by a snapshot, not necessarily the physical space added by that run. Deduplication and compression mean these values are different.

11. Browse the backup without restoring everything

There are three practical ways to inspect content.

List files in a snapshot

sudo -E restic ls latest

Limit the listing to a directory:

sudo -E restic ls latest /home/daniele/Documents

Search for a file

sudo -E restic find '**/invoice-*.pdf'

Search only the latest snapshot:

sudo -E restic find --snapshot latest '**/invoice-*.pdf'

Quote wildcard patterns so that the shell does not expand them before Restic receives them.

Mount the repository with FUSE

Install FUSE support if necessary:

sudo apt install fuse3
sudo mkdir -p /mnt/restic

Mount the repository:

sudo -E restic mount /mnt/restic

Leave that process running and browse `/mnt/restic` from another terminal or file manager. You will find views grouped by snapshot IDs, dates, hosts and tags.

Unmount when finished:

sudo umount /mnt/restic

Never use the repository directory itself, or a path overlapping it, as the mount point.

12. Restore one file or directory

Restoring to a separate empty directory is safer than writing directly over live files.

Create a target:

sudo mkdir -p /var/tmp/restic-restore
sudo chmod 0700 /var/tmp/restic-restore

Restore a single directory from the latest snapshot:

sudo -E restic restore latest \
  --target /var/tmp/restic-restore \
  --include '/home/daniele/Documents/Accounts/**'

Restore a specific file:

sudo -E restic restore latest \
  --target /var/tmp/restic-restore \
  --include '/etc/ssh/sshd_config'

Inspect the restored data before copying it back into production:

sudo find /var/tmp/restic-restore -maxdepth 5 -ls

The restored directory normally contains the original absolute path below the target. For example, `/etc/ssh/sshd_config` will appear under:

/var/tmp/restic-restore/etc/ssh/sshd_config

13. Restore a complete snapshot

Choose a snapshot ID from `restic snapshots`, then restore it to an empty destination:

sudo mkdir -p /restore/full
sudo -E restic restore SNAPSHOT_ID --target /restore/full

Do not point a first full restore at `/`. Restore elsewhere, inspect the result, stop relevant services and copy back only after you have a recovery plan.

A server recovery commonly follows this order:

1. rebuild or boot a clean operating system; 2. recreate storage, users and required packages; 3. install Restic and retrieve the repository password; 4. restore configuration and application files; 5. restore databases using their native tools; 6. validate ownership, permissions, services and network configuration; 7. test the application before reopening access.

14. Read a file directly from a snapshot

For a quick inspection, use `dump`:

sudo -E restic dump latest /etc/ssh/sshd_config | less

Extract a single file:

sudo -E restic dump latest /etc/ssh/sshd_config \
  > /var/tmp/sshd_config.from-backup

You can also dump a directory as an archive:

sudo -E restic dump latest /home/daniele/Documents \
  --archive tar > /var/tmp/Documents.tar

15. Compare snapshots

Find what changed between two snapshots:

sudo -E restic diff OLD_SNAPSHOT_ID NEW_SNAPSHOT_ID

This is useful after a suspected configuration change, accidental deletion or application update.

16. Verify repository integrity

Run the structural check regularly:

sudo -E restic check

This verifies repository metadata, indexes, snapshots, trees and stored pack structure. It does not read every byte of every stored data blob by default.

For a complete data read:

sudo -E restic check --read-data

A full read can be expensive on a large remote repository. Spread verification across several runs:

sudo -E restic check --read-data-subset=1/7
sudo -E restic check --read-data-subset=2/7
sudo -E restic check --read-data-subset=3/7

Continue through `7/7` on later days. This verifies the complete repository in seven portions.

Integrity checking is essential, but it still does not replace an application-level restore test. A database dump may be intact as a file yet unusable because the dump procedure itself was wrong.

17. Define a retention policy

Snapshots accumulate until they are explicitly forgotten. Start with a dry run:

sudo -E restic forget \
  --keep-daily 7 \
  --keep-weekly 5 \
  --keep-monthly 12 \
  --keep-yearly 5 \
  --tag workstation \
  --dry-run

Read the output carefully. When it matches your intention, remove `--dry-run`:

sudo -E restic forget \
  --keep-daily 7 \
  --keep-weekly 5 \
  --keep-monthly 12 \
  --keep-yearly 5 \
  --tag workstation

`forget` removes snapshot references. It does not necessarily recover storage space immediately.

Run `prune` to remove data that is no longer referenced:

sudo -E restic prune

Or combine both operations:

sudo -E restic forget \
  --keep-daily 7 \
  --keep-weekly 5 \
  --keep-monthly 12 \
  --keep-yearly 5 \
  --tag workstation \
  --prune

Pruning may download, repack and upload data, especially with a remote repository. Schedule it less frequently than backups and avoid interrupting it casually.

18. Build a reusable backup script

Create `/usr/local/sbin/restic-workstation-backup`:

sudo tee /usr/local/sbin/restic-workstation-backup > /dev/null <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail

ENV_FILE=/etc/restic/workstation.env
EXCLUDE_FILE=/etc/restic/excludes.txt
LOCK_FILE=/run/lock/restic-workstation.lock

exec 9>"$LOCK_FILE"
if ! flock -n 9; then
    echo "Another Restic backup is already running." >&2
    exit 75
fi

set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a

if ! mountpoint -q /media/backupdisk; then
    echo "Backup disk is not mounted; refusing to continue." >&2
    exit 1
fi

/usr/bin/restic backup \
    /home \
    /etc \
    /usr/local \
    /opt \
    /root \
    /var/backups/database \
    --exclude-file="$EXCLUDE_FILE" \
    --one-file-system \
    --tag workstation \
    --tag automatic

/usr/bin/restic check --with-cache
EOF

sudo chmod 0700 /usr/local/sbin/restic-workstation-backup

Test the script manually:

sudo /usr/local/sbin/restic-workstation-backup

The `flock` guard prevents overlapping runs. The mount-point check prevents a dangerous failure mode in which an absent external disk causes Restic to write a new repository into an ordinary empty mount directory on the system disk.

19. Schedule backups with systemd

Create `/etc/systemd/system/restic-workstation.service`:

[Unit]
Description=Encrypted Restic workstation backup
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/restic-workstation-backup
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7

Create `/etc/systemd/system/restic-workstation.timer`:

[Unit]
Description=Run the Restic workstation backup every day

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

[Install]
WantedBy=timers.target

Enable it:

sudo systemctl daemon-reload
sudo systemctl enable --now restic-workstation.timer

Inspect the schedule:

systemctl list-timers restic-workstation.timer

Run it immediately:

sudo systemctl start restic-workstation.service

Read the log:

sudo journalctl -u restic-workstation.service -n 200 --no-pager

`Persistent=true` causes a missed run to be triggered after the machine next starts. `RandomizedDelaySec` avoids every machine contacting remote storage at exactly the same second.

20. Schedule maintenance separately

Backups should be frequent; pruning can be weekly or monthly.

Create `/usr/local/sbin/restic-workstation-maintenance`:

sudo tee /usr/local/sbin/restic-workstation-maintenance > /dev/null <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail

set -a
# shellcheck disable=SC1091
source /etc/restic/workstation.env
set +a

exec 9>/run/lock/restic-workstation.lock
flock -n 9 || exit 75

/usr/bin/restic forget \
    --keep-daily 7 \
    --keep-weekly 5 \
    --keep-monthly 12 \
    --keep-yearly 5 \
    --tag workstation \
    --prune

WEEK_NUMBER=$(date +%V)
SUBSET=$((10#$WEEK_NUMBER % 4 + 1))
/usr/bin/restic check --read-data-subset="${SUBSET}/4"
EOF

sudo chmod 0700 /usr/local/sbin/restic-workstation-maintenance

Create `/etc/systemd/system/restic-workstation-maintenance.service`:

[Unit]
Description=Restic retention, pruning and partial data verification
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/restic-workstation-maintenance
Nice=15
IOSchedulingClass=idle

Create `/etc/systemd/system/restic-workstation-maintenance.timer`:

[Unit]
Description=Run Restic maintenance weekly

[Timer]
OnCalendar=Sun *-*-* 04:15:00
Persistent=true
RandomizedDelaySec=30m

[Install]
WantedBy=timers.target

Enable it:

sudo systemctl daemon-reload
sudo systemctl enable --now restic-workstation-maintenance.timer

The script derives a quarter of the repository from the ISO week number, so consecutive weekly runs cycle through all four subsets. A separate occasional full-data check is still useful when the repository size and bandwidth allow it.

21. Use an SFTP repository

First configure SSH key authentication to a dedicated backup account. Verify that this works without an interactive password:

ssh backupuser@backup.example.net

An absolute SFTP repository path looks like this:

sftp:backupuser@backup.example.net:/srv/restic/workstation

A relative path under the remote user's home directory omits the leading slash after the host:

sftp:backupuser@backup.example.net:restic/workstation

Set the repository in the environment file:

RESTIC_REPOSITORY=sftp:backupuser@backup.example.net:/srv/restic/workstation

Initialise it:

sudo -E restic init

Then use the same `backup`, `snapshots`, `restore`, `check`, `forget` and `prune` commands.

Use a dedicated account and restrict its SSH capabilities. A backup credential should not grant general administrative access to the remote host.

22. Protect against ransomware and deletion

Encryption protects confidentiality. It does not prevent a compromised client with write and delete access from deleting the repository.

A stronger design separates backup creation from backup deletion:

  • the ordinary client can append new backups;
  • a separate trusted administration host performs retention and pruning;
  • object storage versioning, immutability or snapshots protect repository objects;
  • at least one copy is not continuously writable by the protected machine.

Restic's REST server can be configured in append-only mode. This limits what a compromised backup client can delete, but administrative maintenance still requires full access from a better-protected system.

Be cautious with retention rules on append-only repositories. An attacker able to add misleading snapshots may influence policies based only on the number of daily or weekly snapshots. Time-window retention such as `--keep-within` is safer in that scenario, but operational monitoring and independent storage protection remain necessary.

23. Maintain a second repository

One repository is not a complete disaster-recovery plan. A practical approach is:

  • primary encrypted repository on a local external disk or local backup server;
  • secondary encrypted repository at another physical location;
  • periodic offline or immutable copy for high-value data.

You can back up the same source independently to two repositories. This is simple and gives each destination its own integrity history.

Restic can also copy snapshots between repositories. When creating a destination specifically for copied snapshots, initialise it with the source repository's chunker parameters so that deduplication remains effective:

restic -r /srv/restic-repo-copy init \
  --from-repo /srv/restic-repo \
  --copy-chunker-params

Copy all snapshots:

restic -r /srv/restic-repo-copy copy \
  --from-repo /srv/restic-repo \
  --verbose

For repositories with different passwords, provide the source password with `--from-password-file` and the destination password with the ordinary `--password-file` option or their equivalent environment variables.

The copy process decrypts from the source and encrypts for the destination, so it can consume substantial read, upload and download bandwidth. Test it carefully before relying on it in production, and remember that a repository copy made from the same compromised host is not automatically independent from that compromise.

24. Monitor success properly

A timer that ran is not the same as a backup that succeeded. Monitor at least:

  • the systemd service exit status;
  • the age of the newest snapshot;
  • unexpected changes in backup size;
  • repository check results;
  • free space at the destination;
  • the date and outcome of the latest restore test.

A simple freshness check:

sudo -E restic snapshots --json --latest 1

For automated monitoring, parse the JSON and alert when the newest successful snapshot is older than the agreed recovery-point objective.

Restic normally suppresses interactive progress in a non-interactive context. For periodic status output in logs, set `RESTIC_PROGRESS_FPS` to a low value, for example one update per minute:

RESTIC_PROGRESS_FPS=0.016666

On Unix systems, sending `SIGUSR1` to a running Restic process also requests a progress report.

25. Perform a real restore drill

At least once per quarter, select a representative restore set:

  • one ordinary document;
  • a directory with many files;
  • a file with ownership and permissions that matter;
  • an application configuration;
  • a database dump;
  • a file from an older snapshot rather than only the latest one.

Restore to an isolated target, compare checksums where appropriate, open the documents and actually import the database into a disposable instance.

Example checksum comparison:

sha256sum /home/daniele/Documents/important.pdf
sha256sum /var/tmp/restic-restore/home/daniele/Documents/important.pdf

For a disaster-recovery exercise, perform the restore using only the documentation and credentials that would be available after the original machine had been lost.

26. Common mistakes

The backup disk was not mounted

The mount directory still existed, so the backup filled the system disk. Always test with `mountpoint -q` before starting.

The repository password existed only on the source machine

After losing the machine, the repository was intact but inaccessible. Keep a protected offline copy of the password.

Live databases were copied as ordinary files

A file-level snapshot is not automatically a transactionally consistent database backup. Use native dump or snapshot mechanisms.

Retention was enabled without a dry run

A misunderstood grouping rule removed more snapshots than intended. Run `forget --dry-run`, inspect the groups and document the policy.

Backups succeeded for months but restores were never tested

Configuration errors, exclusions or unusable application dumps went unnoticed. Schedule restore drills as operational work.

The backup client could delete every copy

Ransomware or a stolen credential removed both primary data and backup data. Keep an offline, immutable or separately administered copy.

The repository was copied with an ordinary file synchroniser while active

Copying a repository during concurrent modification can create an inconsistent secondary copy. Prefer independent backups, Restic's repository-copy facilities or a storage-level snapshot taken with appropriate consistency guarantees.

27. A compact operational checklist

For a small Linux system, the minimum credible routine is:

# Daily
restic backup ...

# After every backup or at least regularly
restic snapshots --latest 1
restic check

# Weekly or monthly
restic forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --keep-yearly 5 --prune

# In rotating portions or periodically in full
restic check --read-data-subset=1/4

# Quarterly
restic restore SNAPSHOT_ID --target /isolated/restore-test

Conclusion

Restic makes encrypted, deduplicated and compressed snapshots pleasantly straightforward. The command itself, however, is only one part of a dependable backup system.

The real system includes a protected password, an independently stored repository, sensible exclusions, consistent database dumps, automated execution, monitored failures, retention, repository verification and repeated restore tests.

That is the difference between having backup files and having a recovery plan.

Official references