SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor

Restic backup for VPS: move data comot server

Restic fit send encrypted, deduplicated VPS backups go another server or object storage every night. Learn setup, retention, and restore drill wey prove e work.

Why backup wey dey for the same server no be backup

Restic na free, open source backup tool wey dey send encrypted, deduplicated snapshots of your files go repository for another place: second VPS, machine for house, or S3-compatible object storage. This guide go set am up for Ubuntu 24.04, from installation reach repository over SFTP, first backup, nightly systemd timer, retention policy, and restore drill wey go prove say everything dey work. The destination must be another machine, because copy wey dey for the same server go die together with the server.

A backup/ directory for the box wey e dey back up fit protect you from only one thing: deleting file by mistake. E no go survive failed disk, because e dey on that disk. E no go survive attacker wey get root, because dem go delete the copies first. E no go survive account mistake wey remove the VPS itself. The world's least efficient datacenter dey joke about tarball wey dem name backup_final_v2_REAL wey dey on the same array with the data, and the joke dey work because plenty of us don run exactly that. The rule na make e dey outside the box, and restic na the least painful way to follow am.

Restic for idea four

Repository. Na the place wey restic dey write data go. Na directory for restic own format, full of encrypted blobs, and na only restic fit read am. You no dey edit am by hand; you dey talk to am through restic commands and the -r address.

Snapshot. Na one picture of the files you back up for one exact time. Every backup run dey create snapshot, every snapshot fit restore by itself, and each one dey behave like complete copy of your data for that moment.

Deduplication. Restic dey split files into content-defined chunks and upload only chunks wey repository never see before. The first backup dey upload everything; every run after that dey upload roughly wetin change. Nightly snapshot of 20 GB where 50 MB change go cost about 50 MB, na why keeping dozens of snapshots cheap.

Encryption by default. Restic repository always dey encrypted (AES-256). Restic no gree create or open repository with empty password unless you pass --insecure-no-password (restic 0.17.0 and later), so repository wey you set up like this guide need its password for every command. Backup host or storage provider only dey see encrypted blobs. The serious consequence be say: if you lose the password, the data don go permanently and na by design. Keep copy of the password for somewhere wey no be this server. This matter enough to come up two more times below.

Install restic for Ubuntu 24.04

sudo apt update && sudo apt install -y restic
restic version

For Ubuntu 24.04, dis one go install restic 0.16.4, while the current upstream release na 0.19.1. The difference dey because LTS (long term support) release dey freeze package versions, and e no matter for here: 0.16.4 fit do everything for dis guide. If you want the newest release because of the speed improvements, download the official single-binary build from the restic project's GitHub releases page, unpack am with bunzip2, then install am to /usr/local/bin/restic; nothing else dey for restic installation.

Server wey dey for another machine over SFTP create the repository

You need destination machine: another small VPS na the usual answer, and any machine wey get SSH server plus free disk space go work. Restic dey speak SFTP (file transfer over SSH), so backup host no need install anything. For this guide, backup host na 10.0.0.12 with user wey dem name restic. No name that user backup: Ubuntu and Debian dey ship reserved system account wey dem call backup (uid 34, e no get login shell) for every installation, so adduser backup go fail and ssh backup@... go land for nologin.

The nightly job go run as root for the server wey you dey back up, so root need key login to the backup host. Create dedicated key without passphrase, because nobody go dey there for 3am to type am, then copy am go:

sudo ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N "" -C "web1-restic"
sudo ssh-copy-id -i /root/.ssh/id_ed25519.pub restic@10.0.0.12
sudo ssh restic@10.0.0.12 true && echo key login works

If SSH keys still new to you, SSH key management basics dey explain the model, permissions, and how to revoke key later.

Next na repository password. Generate strong one inside file wey only root fit read:

openssl rand -base64 32 | sudo tee /root/.restic-password
sudo chmod 600 /root/.restic-password

Now copy that password enter your password manager before you continue. If this VPS crash, repository plus this password fit restore everything; repository without the password no fit restore anything.

Initialise the repository:

sudo restic -r sftp:restic@10.0.0.12:/srv/restic/web1 --password-file /root/.restic-password init
created restic repository 9f3c2a1b0d at sftp:restic@10.0.0.12:/srv/restic/web1

The alternative destination na S3-compatible object storage. This one make sense when you no want run second machine. Any S3-compatible bucket go work the same way; na only the address and two credential variables go change:

export AWS_ACCESS_KEY_ID=your-key-id
export AWS_SECRET_ACCESS_KEY=your-secret-key
sudo -E restic -r s3:https://s3.example.com/web1-backups --password-file /root/.restic-password init

Everything after init dey identical for both destinations. The rest of this guide show the SFTP address; replace am with your own.

Backup wey get excludes

Back up the data wey you no fit reinstall, no be the whole filesystem. Operating system go come back when you reinstall am; your configuration and data no go. For normal VPS, that one mean /etc, /home, and anywhere wey your applications dey keep state, like /srv or /var/www. Exclude caches, because dem big, dem dey change every day, and dem fit rebuild by themselves:

sudo restic -r sftp:restic@10.0.0.12:/srv/restic/web1 --password-file /root/.restic-password backup /etc /home /srv --exclude '/home/*/.cache'
Files:        4181 new,     0 changed,     0 unmodified
Added to the repository: 731.204 MiB (312.418 MiB stored)
snapshot 5b8a3f2c saved

The first run go upload everything, so e go take small time. Run the same command again and e go finish within seconds, reporting say some files change and some MiB add, because deduplication only uploads new chunks. List wetin you get:

sudo restic -r sftp:restic@10.0.0.12:/srv/restic/web1 --password-file /root/.restic-password snapshots

Every snapshot show ID, time, and the paths wey e contain. Na those IDs you go use restore from.

Nightly run with systemd timer

Typing the repository address for every command dey tire, and backup wey you dey run by hand go stop within one month. One script and one timer solve both problems. The script set the two environment variables wey restic dey read, RESTIC_REPOSITORY and RESTIC_PASSWORD_FILE, so every command inside am stay short:

sudo nano /usr/local/bin/restic-backup.sh
#!/usr/bin/env bash
set -euo pipefail
export RESTIC_REPOSITORY='sftp:restic@10.0.0.12:/srv/restic/web1'
export RESTIC_PASSWORD_FILE=/root/.restic-password

restic backup /etc /home /srv --exclude '/home/*/.cache'
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check
sudo chmod 700 /usr/local/bin/restic-backup.sh

The forget and check lines dey explained for the next two sections. Now, make we set the schedule: one oneshot service wey go run the script, and one timer wey go trigger am for 03:00 every night. Timer better pass cron line for here because the run dey log to the journal, and Persistent=true go run missed backup as soon as server come back online after downtime.

# /etc/systemd/system/restic-backup.service
[Unit]
Description=Nightly restic backup
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/restic-backup.sh
# /etc/systemd/system/restic-backup.timer
[Unit]
Description=Run the nightly restic backup

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

[Install]
WantedBy=timers.target

Enable the timer, then run the service once by hand and monitor how e dey work:

sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
sudo systemctl start restic-backup.service
sudo journalctl -u restic-backup.service -f

systemctl list-timers show when the next run go trigger. You fit also generate the two unit files instead of typing dem:

ToolGenerate the backup service and timer

The full pattern behind these two files, including calendar syntax and the hardening directives wey service fit carry, dey for running program as systemd service for VPS.

Backup na rumor until you restore am

Treat that sentence like commandment. Backup job wey dey run green every night only prove say job run; e no prove say your data go come back. Two checks go close the gap.

First, restic check, wey the script already dey run every night. E dey verify repository structure and index, so silent corruption for backup host go catch the next night instead of restore day. Once every month, run the deeper version, wey dey download and cryptographically verify random tenth of the actual data:

sudo -i
export RESTIC_REPOSITORY='sftp:restic@10.0.0.12:/srv/restic/web1'
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic check --read-data-subset=10%

Because dem dey choose the subset randomly every time, restic documentation clear say repeated runs no guarantee say dem go cover every pack file, no matter how many times you run am. If you want monthly runs to go through the whole repository without paying for full download, use the fixed group form instead: --read-data-subset=1/10 one month, 2/10 the next, and so on up to 10/10. Restic dey divide the pack files into ten roughly equal groups and check only group n, so full cycle of ten runs go read every group once.

Second, the restore drill. Still inside the root shell from before, restore one real directory from latest snapshot to scratch location and compare am with the live files:

restic restore latest --target /srv/restore-drill --include /etc/ssh
diff -r /etc/ssh /srv/restore-drill/etc/ssh

diff wey no print anything mean say every byte come back identical, and na the only evidence wey count. Delete /srv/restore-drill afterwards. Do this drill every month, and once or twice every year do the full version: restore the entire latest snapshot onto scratch VPS and check say your application actually start from am. The day you need this to work under pressure, you want am to be routine wey you don already do.

Retention: make we forget then prune

If policy no dey, snapshots go dey pile up forever and repository go continue to grow. The script's forget line dey apply policy every night: --keep-daily 7 dey keep one snapshot for each day of the last seven days, --keep-weekly 4 one for each week across four weeks, and --keep-monthly 6 one for each month across six months. Anything wey no rule protect, dem go forget am.

forget by itself only dey remove snapshot records; the data chunks still dey inside repository until something delete dem. Na --prune dey do that: e dey find chunks wey no remaining snapshot reference dey point to and delete dem. Na for that time disk space actually return. Prune dey do real repository work, so for large repository some people dey run forget every night and --prune every week; for normal VPS sizes, every night dey okay.

Databases: make dump first, then back up the dump

Restic dey copy files as e dey read dem, while database dey write to im files continuously. If e capture live database file while write still dey happen, restore fit give corrupt database, because the copy mix pages from before and after the write. The standard fix be this: make database engine produce consistent export to a file, then make restic back up that file.

For PostgreSQL, add one dump line for top of restic-backup.sh, before the restic backup command, and include the dump directory for the backup paths:

mkdir -p /var/backups/db
sudo -u postgres pg_dump myapp | gzip > /var/backups/db/myapp.sql.gz

mysqldump dey do the same work for MariaDB and MySQL. For complete example of the whole pattern, the Nextcloud backup section turn on maintenance mode, dump Postgres, and copy the files as one consistent set. Na exactly that set restic suppose carry commot from the server every night. SQLite na the same idea, but with smaller approach: the Vaultwarden guide stop the container for some seconds to take cold copy of db.sqlite3, and na that archive restic ship commot from the server.

FAQ

Restic backups dey encrypted?

Yes, always. Every restic repository dey encrypted with AES-256 and no unencrypted mode dey. Restic also no gree empty password unless you pass --insecure-no-password (e dey available since restic 0.17.0), so repository wey you set up like this guide need its password for every command. Machine or provider wey dey store the repository only dey hold encrypted blobs, so breached backup host no go expose your files. The trade-off absolute: without the password, nobody fit recover the data, so store copy of am away from the server.

Restic dey do incremental backups?

Every restic snapshot dey behave like full backup, but e dey cost incremental storage. Restic dey split files into chunks and upload only chunks wey repository never store before, so nightly run dey transfer roughly wetin change that day. Unlike traditional incremental schemes, no chain dey to replay: any snapshot fit restore directly, and deleting old snapshot never break newer one.

How I fit restore files from restic backup?

Run restic snapshots to find the snapshot ID, then restic restore <id> --target /some/empty/dir to restore am, adding --include /path to restore only part of am. latest fit work instead of ID. Restic dey recreate the original directory structure under the target, so restoring /etc/ssh go land for /some/empty/dir/etc/ssh. Practice this before you need am, because backup wey you never test na rumor.

How often I suppose run restic backup?

Nightly na sensible minimum for server, and deduplication make am cheap: each run dey upload only chunks wey change since the last one. Data wey dey change fast, or wey losing even one day go cause serious problem, fit run every few hours with the same timer pattern. Frequency na only the easy half; also run restic check regularly and do restore drill every month, because schedule without verification na false comfort.

Wetin go happen if I lose my restic repository password?

The backups no fit recover. Restic encryption get no back door and no reset, so password important as the backups themselves. Keep copy for your password manager and anywhere else durable wey no be the backed-up server. While you still get access, restic key add fit register second password for the same repository, and that one go give you spare.