Why backup restore testing matters
Backup restore testing is what separates a pile of backup files from a recovery plan you can trust. On a dedicated server, the point is not just to run backups on a schedule. You need to prove that a restore works, the right files return, permissions stay intact, services start, and the server is usable after a failure.
This tutorial walks through a practical restore test on a fresh Linux dedicated server. It uses a simple application directory, a PostgreSQL database, and a backup set stored on a separate backup location. The same process works for many hosting setups because it focuses on the recovery sequence, not one specific app stack.
If you are choosing infrastructure for this setup, start with a dedicated server from HostnExtra dedicated servers. For location-specific capacity planning, you can also review United States, Germany, or India depending on where your users and backups need to live.
What you will build and test
- A non-root sudo user for administration
- Basic time sync and package updates
- A small sample app directory and database restore target
- Backup integrity checks with checksums
- A file restore test and a database restore test
- Service startup validation and a rollback path
Assumptions: one fresh dedicated server, SSH access as root, and a separate backup source reachable over the network. Example placeholders used below:
SERVER_IP=203.0.113.10ADMIN_USER=deployDOMAIN_NAME=app.example.comAPP_DIR=/opt/example-appAPP_PORT=8000BACKUP_USER=backupBACKUP_HOST=backup.example.netBACKUP_DIR=/backups/example-app
Connect, identify the OS, and create a sudo user
Run the first SSH command from your local computer. Keep the original root session open until the sudo user test succeeds.
ssh [email protected]This opens the server shell as root. If your provider uses a custom SSH port, replace 22 in the SSH command with the assigned port on your local computer, for example ssh -p 2222 [email protected].
VPS as root: identify the operating system before you install anything.
cat /etc/os-releaseYou should see whether the server is Ubuntu/Debian or AlmaLinux/Rocky Linux. The next steps differ by family.
Ubuntu and Debian family
VPS as root: update packages, install sudo and time sync tools, then create the admin user.
apt update
apt -y upgrade
apt -y install sudo chrony openssh-server rsync jqThis refreshes package lists, applies security updates, and installs the tools you will use later in the restore test. Success looks like completed package output with no held-package errors.
adduser deploy
usermod -aG sudo deployReplace deploy with your chosen admin username. The first command creates the account and asks for a password. The second grants sudo access through the sudo group.
Now create SSH keys for the new user from your local computer if you already have a key, then copy it to the server. If you use ssh-copy-id, run it locally:
ssh-copy-id [email protected]If you prefer manual key placement, log in as the new user, create the SSH directory, and set permissions exactly:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
chown -R deploy:deploy ~/.sshAdd your public key content in the editor, save, and exit. The permissions matter: SSH rejects loose permissions.
Open a second terminal from your local computer and test the new login before you touch root access:
ssh [email protected]
sudo -vYou should land in a shell as deploy and see a sudo password prompt. If that works, keep the original root session open and continue.
AlmaLinux and Rocky Linux family
VPS as root: update packages, install sudo and time sync tools, then create the admin user.
dnf -y update
dnf -y install sudo chrony openssh-server rsync jqThis applies available updates and installs the tools used in the rest of the tutorial.
useradd -m deploy
passwd deploy
usermod -aG wheel deployEnter and confirm a strong password when prompted. The wheel group is the standard sudo path on RHEL-compatible systems.
Set up SSH keys from your local computer using one of these methods:
ssh-copy-id [email protected]Or create the SSH directory manually on the server:
mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
nano /home/deploy/.ssh/authorized_keys
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.sshTest the new account from a second local terminal:
ssh [email protected]
sudo -vOnly continue after the sudo test works.
Prepare the backup source and verify integrity
This step assumes you already have backup archives or database dumps on a separate backup host. The goal is to validate what you restored, not just to download it.
VPS as the sudo user: create a working directory for restore tests.
sudo mkdir -p /srv/restore-test
sudo chown deploy:deploy /srv/restore-testNow pull the backup data from the remote backup server. Replace the archive name with your real file.
rsync -avP [email protected]:/backups/example-app/example-app-files.tar.gz /srv/restore-test/
rsync -avP [email protected]:/backups/example-app/example-app-files.tar.gz.sha256 /srv/restore-test/Check the checksum before you extract anything.
cd /srv/restore-test
sha256sum -c example-app-files.tar.gz.sha256If the checksum is valid, the output should say OK. If it fails, stop and fetch the backup again.
Restore files, database data, and services
Create a placeholder application tree so you can confirm ownership, permissions, and service startup after restore.
VPS as the sudo user:
sudo mkdir -p /opt/example-app
sudo chown -R deploy:deploy /opt/example-appExtract the file backup into the target path.
tar -xzf /srv/restore-test/example-app-files.tar.gz -C /If your archive was built correctly, the files should appear under /opt/example-app. Confirm with:
ls -lah /opt/example-app
find /opt/example-app -maxdepth 2 -type f | headNext, restore the database dump. The example below uses PostgreSQL; if you use MySQL or MariaDB, swap in the matching restore command from your backup procedure.
Ubuntu and Debian family:
sudo apt -y install postgresql
sudo systemctl enable --now postgresql
sudo -u postgres createdb example_app
sudo -u postgres psql example_app < /srv/restore-test/example-app.sqlAlmaLinux and Rocky Linux family:
sudo dnf -y install postgresql-server postgresql
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql
sudo -u postgres createdb example_app
sudo -u postgres psql example_app < /srv/restore-test/example-app.sqlThese commands install PostgreSQL, start it, create a database, and load the SQL dump. A successful run ends with the import completed and no syntax errors.
Set ownership on the restored files if your archive preserved root ownership or incorrect permissions:
sudo chown -R deploy:deploy /opt/example-app
sudo find /opt/example-app -type d -exec chmod 755 {} \;
sudo find /opt/example-app -type f -exec chmod 644 {} \;That normalizes the application tree for a typical web workload.
Validate restore results before you trust the backup
Run these checks from the server after restore. The goal is to prove that files, services, and data are usable, not just present.
ls -l /opt/example-app
systemctl status postgresql --no-pager
sudo -u postgres psql -d example_app -c 'SELECT COUNT(*) FROM information_schema.tables;'Look for:
- Files in the expected directory
- PostgreSQL active and running
- A successful query response, not an authentication error
If you have a web service, create a simple smoke test file and check the port. For a Node, Python, or PHP app, replace this with your real process manager or service name.
python3 -m http.server 8000 --directory /opt/example-appIn another terminal or from your local computer, test the port:
curl -I http://203.0.113.10:8000A valid restore should return an HTTP response. Stop the temporary server with Ctrl+C after testing.
Set up a simple recovery log and rollback path
Record what happened so the next incident is easier to handle. Create a restore note with timestamps, source paths, and results.
cat > /srv/restore-test/restore-notes.txt <<'EOF'
Backup restore test:
- Files restored: /opt/example-app
- Database restored: example_app
- Checksum verified: yes
- Smoke test: passed
EOF
cat /srv/restore-test/restore-notes.txtFor rollback, keep the previous data before overwriting it. A safe pattern is to rename the existing directory and database before restore.
sudo mv /opt/example-app /opt/example-app.pre-restore
sudo -u postgres pg_dump example_app > /srv/restore-test/example_app-pre-restore.sqlIf the restore fails, you can move the old files back and re-import the saved database dump. That is faster and safer than rebuilding from scratch.
Firewall, logging, and persistence checks
Make sure the server stays reachable and the restore survives a reboot.
Ubuntu and Debian family:
sudo systemctl enable chrony
sudo systemctl status chrony --no-pager
sudo journalctl -u postgresql -n 50 --no-pagerAlmaLinux and Rocky Linux family:
sudo systemctl enable chronyd
sudo systemctl status chronyd --no-pager
sudo journalctl -u postgresql -n 50 --no-pagerFor firewalls, allow only the ports you actually use. If this is a database-only restore test, do not open extra public services. If you need SSH and a web test port, open them explicitly with your platform firewall and verify the rules afterward.
Finally, reboot the server during a maintenance window and confirm the restored services return automatically.
sudo rebootAfter reconnecting:
systemctl status postgresql --no-pager
ss -tulpn | grep 8000 || true
curl -I http://203.0.113.10:8000Those checks confirm persistence, listening sockets, and end-user reachability.
Troubleshooting
Checksum fails: run sha256sum -c example-app-files.tar.gz.sha256. If it reports FAILED, fetch the archive again with rsync -avP and do not extract it.
Permission denied after restore: run ls -ld /opt/example-app /opt/example-app/*. If ownership is wrong, correct it with chown -R deploy:deploy /opt/example-app.
Database import error: check the exact failure with journalctl -u postgresql -n 100 --no-pager and review the SQL file for the offending line number. If needed, restore into a fresh database name and compare schemas first.
Service does not start after reboot: check systemctl status postgresql --no-pager and systemctl is-enabled postgresql. If disabled, re-enable it with systemctl enable postgresql.
HTTP smoke test fails: run ss -tulpn to confirm the port is listening, then inspect the application logs. If the service is not running, start it and check why it exited.
Related HostnExtra guides
- Backup Verification on Ubuntu 26.04 and AlmaLinux 10
- Backup Testing Policy for Hosting Teams and VPS Owners
- How to Choose a Dedicated Server in 2026
If you need a dedicated server for backup validation, recovery drills, or production failover planning, start with HostnExtra dedicated servers and choose the region that matches your recovery target.
FAQ
How often should I run backup restore testing?
Run it on a fixed schedule that matches your risk tolerance. Many teams test monthly for files and at least quarterly for full database restores.
Should I test on the production server?
Yes, if you need to confirm the production environment can read the backup and restart services. Use a separate restore path or temporary database name to avoid overwriting live data.
What is the most common restore mistake?
Restoring files without checking permissions and ownership. A backup can be complete and still fail if the app user cannot read or write the restored content.
Do I need a separate backup host?
Yes. Backups should live outside the server being protected. A separate host, storage target, or object store lowers the chance that one failure takes out both the data and the backup.

