How to Test Whether Your Backups Actually Restore

How to Test Whether Your Backups Actually Restore

To test whether your backups actually restore, you restore one into a place that is not your live site and then load it in a browser and use it. The only backup that counts is one you have restored and exercised yourself; every other backup is a hope, not a fact. This article covers how to test website backup restore safely, using the access you already have, without touching production.

Most hosting panels and backup tools give you a restore button. That button is the wrong tool for testing. It overwrites the live site, which is the one thing you are trying to avoid. What you want is a copy of the archive, unpacked somewhere separate, wired to a throwaway database, and reachable at a temporary hostname. That is a few commands and a little DNS, not a support ticket.

First, find out what your backup actually contains

Before you restore anything, open the archive and look. A surprising number of "backups" are a database dump with no uploads, or a file tree with no database, or a tarball that silently skipped a directory because of a permissions error during creation. You cannot test a restore of something that was never captured.

Download the archive to your own machine, then list it without extracting:

tar -tzf backup.tar.gz | head -50
tar -tzf backup.tar.gz | wc -l

Read the paths. You are looking for three things: the application files, the user uploads or media directory, and a database dump. If any of those is missing, stop and fix the backup job before you spend time on a restore drill. Check the tail of the listing too, because a truncated archive often fails near the end rather than the start.

If the archive is a database dump, inspect the header rather than the whole file. A mysqldump output starts with a comment block that names the server version and the dump settings, and it should contain CREATE TABLE statements for every table you expect. A dump that only contains INSERT statements with no schema is a partial dump and will not restore cleanly into an empty database.

Restore into a sandbox, never over the live site

The safe pattern is a second document root and a second database on the same server, or a container on your laptop. Either works. The point is that the live site and the test site share nothing: not the database, not the files, not the session cookies.

Extract the archive into a fresh directory:

mkdir -p /var/www/restore-test
tar -xzf backup.tar.gz -C /var/www/restore-test
ls -la /var/www/restore-test

Then create an empty database and load the dump into it. Do not load it over your production database, and do not load it over a database that already has tables from a previous test, because leftover tables hide missing ones.

mysql -u root -p -e "CREATE DATABASE restore_test CHARACTER SET utf8mb4;"
mysql -u root -p restore_test < dump.sql
mysql -u root -p restore_test -e "SHOW TABLES;"

Compare the table list against production. Missing tables are the single most common reason a restore looks fine and then breaks a week later when someone visits a page that queries the absent table.

Now point the test copy at the test database. This is where most people get caught. The application's config file still holds the production credentials and the production hostname. Edit the config in the extracted copy only. In a typical PHP application that is a file such as wp-config.php or .env; in other stacks it may be a settings.py, a config/database.yml, or an environment file read at boot.

define('DB_NAME', 'restore_test');
define('DB_USER', 'restore_user');
define('DB_PASSWORD', 'REDACTED');
define('DB_HOST', 'localhost');

Two more things belong in that config for a test copy: debug output on, and outbound email off. You do not want a restore test sending password reset mail to real users or indexing itself in a search engine. Most frameworks have a mail transport setting you can point at a null handler or a local sink, and a noindex header you can send from the web server for the whole test hostname.

Serve the copy on a separate hostname

Give the test copy its own name, for example a subdomain of a domain you already control, and point it at the extracted directory with its own server block or virtual host. Keep it out of search results and keep it behind authentication if it contains real customer data.

server {
    server_name restore-test.example.com;
    root /var/www/restore-test;
    add_header X-Robots-Tag "noindex, nofollow";
    auth_basic "test";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

The reason for a separate hostname rather than a path under the live site is that many applications hardcode their base URL in the database, in a settings row, or in generated links. Serving the copy at its own name lets you update that value in the test database and confirm the site works end to end, rather than fighting redirect loops caused by a mismatch between the stored URL and the URL you are browsing.

What to actually check once it is running

Loading the home page is not a test. Walk through the things that touch the database and the filesystem. Log in as an administrator. Open a page that renders user uploaded images and confirm the images appear, which proves the uploads directory was captured and the paths still resolve. Submit a form that writes a row, then query the test database directly to confirm the row landed. Open the newest and oldest content in the archive, because backups frequently capture old data and miss recent writes. Check that plugins, themes, or modules are present and that their own tables came across.

Then read the error log while you click. A restore that renders a page but logs a stream of warnings about missing files or failed queries is not a working restore. It is a restore that will fail in front of a customer.

Record what you did: the archive you used, the date it was taken, the steps you ran, and anything that was missing. That record is what turns a one off drill into a procedure you can repeat, and it is the evidence you need when a restore matters.

Finally, decide how often to repeat this. The right cadence depends on how often your site changes and how much a day of lost data would cost you, but the drill should be frequent enough that you are still fluent in it when you are under pressure. If your host offers a staging clone feature, use it for the routine check and keep the manual procedure for the times the panel is unavailable. Either way, put the next drill in the calendar now, and until you have restored a backup and used the restored site, treat your backups as unverified.

Related articles

Subscribe to our newsletter

Get the latest hosting tips, performance insights, and industry news.