This guide shows you how to run restic encrypted backups on a Linux VPS: initialise a repository on a second machine, take snapshots, list them, and restore data to a fresh server. Restic encrypts everything on the client before it leaves the box, so the storage host only ever sees ciphertext.
You will use two servers: the source VPS that holds your data, and a second VPS that stores the repository. If you only have one server, you can still follow along with a local repository path, but off-site storage is the point of the exercise.
Before you start
- Two Linux VPS running Ubuntu 22.04/24.04, Debian 12, or AlmaLinux/Rocky 8–9.
- Root or sudo on both. The examples use
203.0.113.10as the storage host. - SSH access from the source server to the storage server.
- A password manager. The repository password is the only key to your data.
Warning: Restic has no password recovery. If you lose the repository password, every snapshot is permanently unreadable. Store it somewhere other than the server you are backing up.
Install restic on both servers
On Debian and Ubuntu:
sudo apt update
sudo apt install -y resticOn AlmaLinux and Rocky:
sudo dnf install -y resticCheck the version with restic version. Package versions differ between distributions, and restic changes repository behaviour occasionally, so check the project's own documentation if you need a specific release.
Prepare the storage host
On the storage server, create a dedicated user and directory. Keeping the repository under a separate account means a compromised source server cannot wander through the rest of the filesystem.
sudo adduser --disabled-password --gecos "" restic
sudo mkdir -p /srv/restic-repo
sudo chown restic:restic /srv/restic-repo
sudo chmod 700 /srv/restic-repoBack on the source server, generate a key pair for the backup job and copy the public half across.
ssh-keygen -t ed25519 -f /root/.ssh/restic_backup -N ""
ssh-copy-id -i /root/.ssh/restic_backup.pub restic@203.0.113.10
ssh -i /root/.ssh/restic_backup restic@203.0.113.10 "echo connected"The last command should print connected. If it asks for a password, the key was not installed correctly.
Initialise the restic encrypted backup repository
Create the password file first, then point restic at the SFTP backend.
openssl rand -base64 32 > /root/.restic-pass
chmod 600 /root/.restic-pass
export RESTIC_REPOSITORY="sftp:restic@203.0.113.10:/srv/restic-repo"
export RESTIC_PASSWORD_FILE=/root/.restic-pass
restic -o sftp.command="ssh -i /root/.ssh/restic_backup restic@203.0.113.10 -s sftp" initExpect output like created restic repository 4f2a... at sftp:restic@203.0.113.10:/srv/restic-repo. The -o sftp.command flag tells restic which SSH key to use; without it, restic falls back to your default key.
Tip: Exporting RESTIC_REPOSITORY and RESTIC_PASSWORD_FILE in your shell saves typing, but scripts should set them explicitly so cron jobs work without a login shell.
Take your first snapshot
Run a backup of the directories that matter. Exclude caches and anything you can rebuild.
restic -o sftp.command="ssh -i /root/.ssh/restic_backup restic@203.0.113.10 -s sftp" backup \
/etc /home /var/www \
--exclude-caches \
--exclude /var/www/*/var/cache \
--tag dailyRestic prints a progress line and finishes with a summary: files new, files changed, data added, and total duration. The first run uploads everything; later runs only send changed blocks, so they are much faster.
List what you have:
restic snapshots
restic ls latest
restic statsrestic snapshots shows the snapshot ID, timestamp, host and tags. restic ls latest lists the contents of the most recent snapshot. restic stats reports total size and file count.
Automate snapshots and pruning
Create a small script so cron does not depend on your shell environment.
sudo tee /usr/local/bin/restic-backup.sh > /dev/null <<'EOF'
#!/bin/bash
set -euo pipefail
export RESTIC_REPOSITORY="sftp:restic@203.0.113.10:/srv/restic-repo"
export RESTIC_PASSWORD_FILE=/root/.restic-pass
export RESTIC_OPTIONS="-o sftp.command=ssh -i /root/.ssh/restic_backup restic@203.0.113.10 -s sftp"
restic backup /etc /home /var/www --exclude-caches --tag daily
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
EOF
sudo chmod 700 /usr/local/bin/restic-backup.shThen schedule it:
echo "0 3 * * * /usr/local/bin/restic-backup.sh >> /var/log/restic-backup.log 2>&1" | sudo tee /etc/cron.d/restic-backupforget with --prune removes old snapshots and reclaims space. Without pruning, the repository grows forever.
Restore data to a new server
On the replacement server, install restic, copy the password file and the SSH key, then read the repository.
sudo apt install -y restic
scp root@203.0.113.10:/root/.restic-pass /root/.restic-pass
scp root@203.0.113.10:/root/.ssh/restic_backup /root/.ssh/restic_backup
chmod 600 /root/.restic-pass /root/.ssh/restic_backup
export RESTIC_REPOSITORY="sftp:restic@203.0.113.10:/srv/restic-repo"
export RESTIC_PASSWORD_FILE=/root/.restic-pass
restic snapshotsRestore into a staging directory first so you can inspect the result:
restic restore latest --target /mnt/restoreTo pull back a single path, use --include:
restic restore latest --target /mnt/restore --include /var/www/example.comWarning: Restoring with --target / writes straight over live files and can break a running service. Restore to a staging directory, compare, then move files into place with the service stopped.
If you only need a few files, mount the repository instead of restoring everything:
mkdir -p /mnt/restic
restic mount /mnt/restic
ls /mnt/restic/snapshots/latest/var/wwwPress Ctrl+C to unmount when you are done.
Check it works
Confirm the repository is intact and readable:
restic check
restic check --read-datarestic check verifies structure and metadata. --read-data downloads and hashes every pack file, which proves the data itself is recoverable but takes a long time on a large repository. Run it occasionally, not every night.
Then compare snapshots to see what changed:
restic diff latestA successful restore test is the only real proof. Restore one directory into /tmp, open a file, and confirm the contents match the source.
Troubleshooting
Fatal: unable to open config fileorwrong password—RESTIC_PASSWORD_FILEis unset or points at the wrong file. Check withecho $RESTIC_PASSWORD_FILEand confirm the file is readable by the user running restic.sftp: command not foundor SSH key rejected — the storage host has the SFTP subsystem disabled, or the key permissions are wrong. Runchmod 600 /root/.ssh/restic_backupand test the SSH command on its own.repository is already locked— a previous run was killed mid-backup. Confirm no restic process is running, then clear the lock withrestic unlock.- Backups saturate the uplink — throttle with
--limit-upload 2048(KiB/s) and wrap the job innice -n 19 ionice -c3. - Restored files are missing — the path was excluded at backup time. Run
restic ls <snapshot-id>to see exactly what the snapshot contains before assuming data loss.
Next steps
- Combine file-level and database backups: Back up your Linux VPS with rsync, restic and cron.
- Schedule database dumps alongside restic: Automating MySQL backups with mysqldump and cron.
- Practise a database restore: Restore a MySQL Database from a mysqldump File.
