Restore a VPS backup

3 min read07 November 2026

A backup only protects you if the restore works. When you actually need one, you are usually under pressure: a service is down, an update broke a configuration. This procedure starts from a local archive or a dashboard backup and brings the service back step by step, without destroying anything that might still be useful.

Check the archive before touching the server

First confirm what you are about to restore. A truncated archive or one from another date only adds confusion.

Bash
sudo ls -lh /root/backup-2026-11-01.tar.gz
sudo sha256sum /root/backup-2026-11-01.tar.gz
sudo tar -tzf /root/backup-2026-11-01.tar.gz | head -n 20

Compare the checksum with the one recorded when the backup was taken. Read through the path list: you should recognise /var/www, /etc/nginx or the relevant application. Then check free space with df -h /: you need room for the archive and for the extracted files.

Preserve the current state before replacing it. Renaming a directory takes seconds and prevents a permanent loss:

Text
sudo mv /var/www/my-site /var/www/my-site.before-restore

Restore files

Stop the service writing to those files first. For a static site Nginx can keep running; for an application, stop it.

Bash
sudo systemctl stop my-app
sudo tar -xzpf /root/backup-2026-11-01.tar.gz -C /

The -p flag restores permissions and owners when the extraction runs as root. If the archive was created with relative paths, extract it under the right parent. For a single directory, rsync is more selective:

Bash
sudo rsync -aHAX --numeric-ids /root/restore/var/www/my-site/ /var/www/my-site/

The trailing slash on the source means "the contents of", not the directory itself.

Restore a database

Recreate the database if it no longer exists, then import the dump. A dump taken with --single-transaction is consistent without blocking writes at backup time.

Bash
sudo mysql -e "CREATE DATABASE IF NOT EXISTS app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
gunzip < /root/app-2026-11-01.sql.gz | sudo mysql app

On a large database, import into a temporary database first and compare row counts of the main tables before replacing production.

Verify the service

Restart and check three layers: the process, the network, the application.

Bash
sudo systemctl start my-app
sudo systemctl status my-app --no-pager
sudo journalctl -u my-app -n 50 --no-pager
curl -sS -o /dev/null -w '%{http_code}\n' https://your-domain.tld/

Then open the application and perform a real write: log in, submit a form, upload a file. Check web file permissions (644 for files, 755 for directories) and the owner expected by the web server.

Good practice

  • Restore with the same major version of the software and database engine as the backup.
  • Test a full restore into a staging directory at least once a quarter.
  • Encrypt archives that contain secrets, then copy them off the VPS: an archive stored only on the machine it protects does not survive a reinstall.
  • Record the date, contents and location of each backup in a tracked file, not in your memory.

Advertisement

Was this article helpful?

Read next