Backing up a VPS, and testing the restore
A working backup means one you have tested. This guide covers platform-level backups through Virtualizor and file-level backups with restic, then walks through restore testing to prove both methods work.
Backing up a VPS well means two things at once. There is the whole-machine image that our Virtualizor backup plans can take at the platform level, and there is the file-level backup you run yourself inside the server with a tool like restic, which is what lets you pull back one config file or one database from last Tuesday. Neither counts as a backup until you have restored something from it and compared the result against the original, so the second half of this guide is about proving the thing works rather than assuming it does.
Before you start
This picks up from a VPS you can already reach over SSH with a user that can use sudo. If you cannot get in yet, start with our VPS guides and come back.
Commands below are given for AlmaLinux 9 and Ubuntu 24.04. You also need somewhere to put the backups that is not the VPS itself. A second disk on the same machine protects you from a bad deploy, not from losing the machine. Object storage, another server, or a machine in your office all work.
Five things in this topic can ruin your day, so they are worth reading before you type anything:
- A Virtualizor restore overwrites the running VPS. It is an image restore, not a merge. Anything written since that dated backup is gone.
- Never restore to
/as a test. Restore to a scratch directory and compare. Restoring over a running system is how a restore test becomes an outage. - Copy a damaged repository before you repair it. restic's own troubleshooting guide puts that first, ahead of any repair command, and it is right.
- Do not prune or forget snapshots as a tidy-up step. That is irreversible deletion. Verify first, delete later, and use
--dry-runbefore you trust a retention rule. - A restic repository format upgrade is one way. It raises the minimum restic version needed to read the repository, which matters if you have machines on different versions.
Step 1: Decide what you are actually protecting
Write down two numbers before choosing tools. How much data can you afford to lose, in hours? And how long can the service be down while you rebuild? A blog that changes weekly and a shop taking orders every minute need different answers.
Then list what has to come back for the service to work. On a typical web VPS that is: the site files (often under /var/www or /home), the database, the web server and PHP configuration under /etc, TLS certificates, cron jobs, and any systemd unit files you wrote yourself. Backing up the whole filesystem including /proc, /sys and /tmp wastes space and restores badly.
Honest answer time: if your VPS holds nothing but a stock install and a couple of config files you keep in Git, you may not need a file-level backup regime at all. Platform-level backups plus a documented rebuild is enough. Most people do have state worth keeping, which is why they are reading this.
Step 2: See what Virtualizor already covers
VPS backups on our platform are driven by a backup plan configured on the node, and there has to be at least one backup server attached before VPS backups can run at all. Where a plan allows it, the customer-facing Virtualizor panel (reached from your Hostworld client area) shows a dated list of backups for the VPS, and starting a restore from one of them is a matter of picking the date and confirming. Whether initiating backups and restores yourself is enabled, and any monthly allowance on them, depends on the plan attached to your VPS, so look at the backups section for your VPS in Virtualizor rather than assuming.
If the backup you need is not available to you there, or you want a restore performed at a specific time, open a support ticket and say which VPS and which date. We can also read the node-side backup and restore logs, which is usually how an odd failure gets diagnosed.
The important warning again: a Virtualizor restore replaces the current disk contents with the backup. Before restoring anything, take a fresh backup of the current state if you can. That gives you a way back if you pick the wrong date.
Step 3: Install restic
restic is a single binary that does deduplicated, encrypted, incremental backups to local disk, SFTP or object storage. It is a good fit for a VPS because the repository is encrypted before it leaves the machine.
AlmaLinux 9. restic lives in EPEL rather than the base repositories, so enable that first. The first command adds the EPEL repository definition, the second installs restic, the third prints the version:
sudo dnf install -y epel-release
sudo dnf install -y restic
restic version
Ubuntu 24.04. restic is in universe. This refreshes the package lists, installs restic, then prints the version:
sudo apt update
sudo apt install -y restic
restic version
Compare what restic version prints against the current release on restic.net. Noble ships 0.16.4, while the current stable release at the time of writing is 0.19.1 (July 2026). That gap matters in two directions. Features you may want are missing, notably the snapshot filters that 0.19.0 added to restic check, and a repository written by a much newer restic elsewhere may not be readable by 0.16.4.
If the packaged version is too old for you, the restic project's own advice is to use the official binary, which is current and built reproducibly. Binaries installed that way can be updated in place:
sudo restic self-update
That command only works on a binary you installed as a binary. It will not update a package installed by dnf or apt.
What about BorgBackup?
Borg is a reasonable alternative and works on the same principles. If you choose it, use the 1.4 stable series (1.4.5 is the current release). Borg 2.0 is still in testing and the project labels its builds as not for use on production backup repositories. Borg 2.0 will also be a breaking change: repositories are not directly compatible with 1.x, the command line changed, and moving existing archives across needs borg transfer, which itself requires you to be on 1.2.6 or later and to have followed the changelog's manifest and archive upgrade instructions first. The rest of this guide uses restic.
Step 4: Create a repository and take the first snapshot
Set the repository location and a password file. Losing the password means losing the backups, so store it somewhere other than this VPS as well. These commands create the password file with restrictive permissions and export the two variables restic reads:
sudo sh -c 'umask 077; head -c 32 /dev/urandom | base64 > /root/.restic-pass'
export RESTIC_REPOSITORY=/mnt/backup/restic
export RESTIC_PASSWORD_FILE=/root/.restic-pass
Now initialise the repository. This writes the repository structure and key material, and does nothing to your data:
sudo -E restic init
Take the first snapshot. This reads the listed paths, skips anything matching the excludes inside them, and stores the result with a tag you can filter on later:
sudo -E restic backup /etc /home /var/www --exclude '/var/www/*/cache' --tag daily
One behaviour change to know about if you are copying an old script: from restic 0.19.0 onwards, exclude rules no longer apply to the paths you name on the command line itself, only to the contents within those paths. Confirm the snapshot exists:
sudo -E restic snapshots
Step 5: Dump the database consistently
Copying live MySQL or MariaDB data files gives you a torn, unusable database. Dump it to a file first, then let restic pick the file up. This writes a single dump of everything, taking a consistent read rather than locking tables, and streams rows rather than buffering them:
sudo mysqldump --single-transaction --quick --all-databases > /var/backups/mysql-all.sql
Three caveats the manual is clear about. A consistent read is not isolated from DDL, so ALTER TABLE, CREATE TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE and friends running during the dump can make it wrong or make it fail: do not dump while a migration is running. --single-transaction and --lock-tables are mutually exclusive, because locking tables implicitly commits the pending transaction. And combining --single-transaction with --set-gtid-purged is unsafe on MySQL 5.7 and on 8.0 before 8.0.32, where it could produce inconsistent output; it was fixed in 8.0.32.
Also worth knowing on a busy server: --single-transaction holds a metadata lock on each table until the dump finishes, so DDL is blocked for the duration, and blocked DDL can then block ordinary queries behind it. Dump large databases when the site is quiet.
Back the dump up along with everything else:
sudo -E restic backup /var/backups/mysql-all.sql --tag db
Step 6: Check the repository, and know what the check proves
restic has two levels of verification and the difference is the whole point of this guide. The plain check verifies structural consistency and integrity of snapshots, trees and pack files. It does not read the contents of the pack files, because that would mean downloading a copy of every one:
sudo -E restic check
To verify the stored data itself, add --read-data. This downloads every pack file and re-verifies it, so it costs time and bandwidth, which matters on a metered or remote backend. Do not fire it off casually on a production VPS:
sudo -E restic check --read-data
The practical middle ground is to check a fraction each night and cover the repository over a week. This reads the first of seven parts today; run 2/7 tomorrow and so on:
sudo -E restic check --read-data-subset=1/7
On restic 0.19.0 and later, check also accepts snapshot filters such as --tag, --host and --path, or explicit snapshot IDs, so you can verify just the snapshot you care about.
If a check reports damaged or missing pack files, or unreadable snapshot files, restic prints instructions pointing at repair packs or repair snapshots. Before you run either, make a full copy of the repository if you have the space, or at the very least copy its index and snapshots folders. Repairing the only copy of a damaged repository can turn a recoverable problem into a permanent one.
Step 7: Restore a file and compare it (the actual test)
Structural and data checks tell you the repository is intact. They do not tell you that you can get your service back. The only thing that proves that is a restore.
Restore into a scratch directory, never over the live filesystem. This creates a target directory and pulls one path out of the most recent snapshot:
sudo mkdir -p /tmp/restore-test
sudo -E restic restore latest --target /tmp/restore-test --include /etc/nginx
Now compare what came back against what is on the server. diff -r walks both trees and reports any difference:
sudo diff -r /tmp/restore-test/etc/nginx /etc/nginx
Do the same for the database dump, and then prove it loads. This restores the dump file and imports it into a scratch database rather than over the live one:
sudo -E restic restore latest --target /tmp/restore-test --include /var/backups/mysql-all.sql
sudo mysql -e "CREATE DATABASE restore_test;"
sudo mysql restore_test < /tmp/restore-test/var/backups/mysql-all.sql
Check a table count or a known row, then drop the scratch database. Clear out /tmp/restore-test when you are done so you are not keeping a plaintext copy of your data lying around.
Put a date in a note when you do this. A restore test from fourteen months ago is not evidence.
Step 8: Test the full recovery, not just one file
Once a year, or after any significant change to the stack, prove you can rebuild the whole thing. The clean way is to build a second small VPS, install the same operating system, restore from the repository onto it, and see whether the site comes up. That costs you a few pounds and an afternoon and tells you more than any check command.
If you instead test by restoring a Virtualizor backup over the VPS, understand that you are overwriting the live server. Take a fresh backup first, do it in a window where downtime is acceptable, and if you want us to stand by while it happens, open a support ticket beforehand rather than during.
Step 9: Schedule it, then set retention last
Wrap the backup and the dump into a script, run it from a systemd timer or cron, and have it write output to a log you actually read. Add the weekly --read-data-subset check to the same schedule.
Only once verification is in place should you think about deleting old snapshots. forget and prune remove data permanently. Always see the plan first:
sudo -E restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --dry-run
Read the list of snapshots it says it would remove. If you are happy, run it again without --dry-run, then restic prune to reclaim the space. Pick numbers that suit your own retention needs rather than pasting mine.
One last version note. If you ever run restic migrate upgrade_repo_v2, followed by prune to compress metadata, that is a one-way change and it raises the minimum restic version required to read the repository (format version 2 needs 0.14.0 or newer). Repository problems have to be corrected before a migration will run at all. If some of your machines are on Ubuntu 24.04's 0.16.4 and others are on a current binary, decide on one version across the fleet before migrating anything.
What next
The next step in this series is Monitoring a VPS: disk, memory and service alerts, which is how you find out a backup job stopped running before you need the backup. You will find it with the rest of our VPS guides, alongside the Virtualizor walkthroughs for rebuilds and console access.
If you want us involved at any point, whether that is checking a backup plan on your VPS, reading the node-side backup logs, or running a restore to a date you give us, open a support ticket. Tickets are logged against your account, so the history is there next time.
Common questions
Is the Virtualizor backup enough on its own?
It covers the case where the machine is lost or badly broken, and it restores fast. It does not give you a single config file from last Tuesday, and restoring it overwrites the whole VPS. Most people want both: platform backups for disaster, restic for granular recovery.
Does restic check mean my backup works?
It means the repository is structurally consistent. Add --read-data or a rotating --read-data-subset to verify the stored data itself. Neither proves you can recover, which is why Step 7 restores a file and compares it.
Can I restore my VPS from a backup myself?
Look at the backups section for your VPS in Virtualizor. Whether you can start a restore yourself depends on the backup plan attached to your VPS, and plans can allow backups, restores, both or neither, sometimes with a monthly limit. If what you need is not there, send us a ticket with the VPS and the date.
Why is the restic in Ubuntu's repositories so old?
Ubuntu 24.04 ships 0.16.4 and keeps it on security updates for the life of the release, while upstream has moved on to 0.19.x. That is normal for a long-term support distribution. Use the official binary if you need current features, and keep the version consistent across machines sharing a repository.
Should I use Borg 2.0 instead?
Not yet. The Borg project marks 2.0 builds as beta and says not to use them on production backup repositories. Stay on the 1.4 series, and plan the 2.0 move properly later, because repositories are not directly compatible and archives have to be moved with borg transfer.