Skip to content
System nominalUTC --:--:--Articles 124

A Restore Drill You Can Run in Thirty Minutes a Month

A backup proves nothing until it is restored under a clock. The monthly drill that measures how long recovery takes and what breaks quietly.

Tomas Lindqvist · @tlindqvist

Hardening and operations

Topic
INFRA
Section
Hosting & Infrastructure
Published
20 OCT 2026
Reading time
8 MIN
Words
1832W
Technical cover graphic for A Restore Drill You Can Run in Thirty Minutes a Month
Generated schematic — INFRA series, REV 2.6.

The first restore took four hours and produced a site that looked correct. The second took forty minutes, because the operator had written down the path of the archive, the name of the database and the three commands that rebuilt the search index. The difference was not skill. It was that the first was an incident and the second was a drill, and the drill had already discovered the parts nobody remembered.

Restore testing has a reputation for being expensive, which is why it gets postponed. The version that actually works is small: once a month, take one archive, restore it into a scratch copy, walk a fixed list of screens, and write down the time. Thirty minutes is enough for a small site. The value is not the restored site. It is the knowledge of how long the restore takes and what fails when nobody is under pressure.

This is the companion to the backup articles: one covers where the copy lives, another what it must contain. This one covers the only step that turns either of those into a fact.

Disclosure: some links on this page are affiliate links, and WPSifero may earn a commission if you buy through them. The drill below works with any backup source, including the free one: a dump, a tar and a spare database.

A drill is a rehearsal with a clock, not a restore

During an incident, the goal is a working site as fast as possible. During a drill, the goal is information. Those are different jobs, and mixing them produces the worst of both: a restore done quickly enough to feel successful and loosely enough to prove nothing.

So the drill has a scope and a success criterion written before it starts. Scope: one archive, one scratch environment, one operator. Success: every item on the smoke list passes, or the failure is recorded with the step that produced it. Time is measured from "start" to "the last smoke item passes", not from "start" to "the files are copied".

The clock matters because recovery time is a business number. A four-hour restore is fine for a brochure site and fatal for a store on a Saturday. Knowing the number before an incident is how the decision about a warm standby, a paid restore service or a simpler site gets made with evidence rather than anxiety.

Restore the oldest copy you have, not last night's

The obvious drill restores the newest archive, and it is the least informative one. Newest archives are the most likely to be intact, because they were written by the current configuration with the current credentials. The failures live at depth: the lifecycle rule that deleted last quarter, the encryption key that was rotated, the incremental chain with a missing link.

Rotate the target instead. Month one, the newest archive. Month two, the archive from thirty days ago. Month three, the oldest archive in the retention window. Month four, a copy from the off-host bucket rather than the local one. Over a year that covers every route the recovery might take, and it finds the failures while the site is healthy.

Choose the scenario at the same time. A drill that always assumes "the site was deleted" misses the compromise case, where the restore target must predate the infection. A drill that always assumes "bad plugin update" misses the account suspension, where the backup has to be reachable without the host. Write the scenario in the log, because the same archive can be adequate for one and useless for the other.

Restore into a scratch copy, never over production

The drill is not allowed to touch the live site. Use a staging environment, a local environment or a temporary subdomain with basic authentication. A drill that can take down production will eventually do it.

Thirty minutes, eight steps

The sequence below assumes a staging install that shares nothing with production except the code. If the host provides staging, use it. If not, a local environment with the same PHP version is enough, and the local environment article covers the matching part.

  1. Start the clock: write the archive timestamp and the scenario in the log before touching anything.
  2. Create the empty target: a database and a document root, both disposable.
  3. Import the database: stop on the first error, and do not continue past a failed import.
  4. Extract the files into the document root, then confirm the file count against the archive listing.
  5. Point the configuration at the target: database credentials, staging URLs and a staging environment type.
  6. Rewrite the URLs with one command, serialized data included, before opening the site.
  7. Run the smoke list: every item, in order, with the result recorded.
  8. Stop the clock: write the elapsed time and the defects before fixing anything.
cd /var/www/staging
wp db import /tmp/site-db.sql
wp search-replace 'https://example.com' 'https://staging.example.com' \
  --all-tables --precise --skip-columns=guid
wp config set WP_ENVIRONMENT_TYPE staging
wp option update blog_public 0
wp cache flush
wp rewrite flush

The rewrite step is where drills usually go wrong. A plain SQL UPDATE on the options and posts tables will corrupt serialized values, so use a tool that unserializes and reserializes, which is what wp search-replace does. Skip the GUID column: it is an identifier, not a URL to be rewritten, and changing it breaks feed readers and a few integrations.

The smoke list that catches a half-restore

The homepage is the least useful check on the list. It is static, cached and usually rendered from the options table, which is the part every restore gets right. The failures are in the screens that read the tables and files nobody thought about.

CheckWhat it provesHow
Login and adminSalts, users and sessions are coherentLog in with a real account
Permalinks.htaccess and rewrite rules survivedOpen a post, a page and a category
MediaUploads and thumbnails are presentOpen the library, insert an image
CheckoutCustom order tables and sessions workPlace a test order to a test gateway
MailSMTP configuration and DNS are intactPassword reset to a mailbox you control
CronScheduled tasks exist and can runList events, run one non-destructive hook
Forms and searchCustom tables and indexes are readableSubmit a form, run a search
IntegrationsAPI keys and webhook endpoints resolveSend a test event to a sandbox

Useful commands for the list, run against the staging install:

wp option get siteurl
wp plugin list --status=active --fields=name,version
wp post list --post_type=page --fields=ID,post_title,post_status
wp cron event list --fields=hook,next_run_relative
wp db query "SELECT COUNT(*) AS orders FROM wp_wc_orders;"

The last query only applies to a store using HPOS, and that is the point: the smoke list has to name the tables this site actually uses. A generic list misses the plugin that keeps bookings in its own table, and that plugin is exactly the one whose failure appears a week after the restore.

One warning about cron and mail. Running due cron events on a restored copy can send real email, charge a card through a stored token or post to a live webhook. Disable outgoing mail at the server or plugin level before the drill, and point integrations at sandbox endpoints. A drill that accidentally emails customers is its own incident.

Record three numbers, not a feeling

The log is one page, and it has three numeric fields. Recovery time: start to the last passing check, in minutes. Restore point age: how old the archive was at the moment of the restore, which is the real recovery point objective, not the setting on the backup screen. Defects: the count and the names of the checks that failed. Everything else is commentary.

Three numbers make the trend visible. If recovery time is climbing, something in the environment is getting slower or the steps are getting longer. If the restore point age is consistently older than the backup schedule promises, the job is running late or failing quietly. If defects cluster around one area, that area needs a different backup method rather than another drill.

Keep the log outside the site, in the same place as the runbook and the credentials path. A log stored in a plugin dashboard disappears with the site, which is precisely when it is needed. A shared document with a date per row is enough.

What a drill cannot prove

The drill proves that this archive restores into this staging environment in this many minutes. It does not prove that the DNS cutover works, that the provider will answer during a regional outage, or that the person on call can find the credentials when the site is down. Those are separate exercises, and the disaster recovery runbook covers them.

It also does not prove that the restored site is clean. Restoring a backup taken during a compromise reproduces the compromise, faithfully and quickly. Pair the restore drill with a check of what the restored copy contains: modified timestamps in wp-content/uploads, unexpected administrators, unknown cron hooks. The drill measures recovery, not integrity.

Finally, it does not remove the human failure. The first real incident is still stressful, and the operator will still forget something. The drill reduces the number of things that can be forgotten to the ones written on the page. That is the whole benefit, and it is worth thirty minutes a month.

Who runs it, and when

Name one owner. On a small site it is the person who already pays the hosting bill; on a team it is the person on call for the week. Put the drill in the calendar as a recurring event with the staging URL and the log link in the description, so the step from intention to action is one click.

The first drill will take longer than thirty minutes, because the staging environment does not exist yet and the steps are being written. That is expected. The second is faster, and by the fourth the whole exercise fits in a lunch break. If the host sells a one-click staging copy and a restore button, the drill is mostly clicking two buttons and walking the smoke list, which is the version most teams actually sustain.

The paid options are worth naming for what they change. Jetpack's backup service provides the restore point and a restore flow from outside WordPress, which removes the dependency on a working admin. Pressable provides staging as part of the hosting, which is the piece most drills lack. Neither replaces the drill; they shorten it.

If the site is a brochure site with a host snapshot and no custom tables, a quarterly drill of the newest archive is proportionate. If it takes money, monthly, with the oldest archive once a year. The decision rule: the drill schedule is set by how much a day of downtime costs, divided by the cost of thirty minutes. For most sites that ratio makes the monthly drill almost free, and the first incident that goes according to plan pays for a decade of them.

Tomas Lindqvist · @tlindqvist

Hardening and operations

Threat models plugins, audits capability and nonce handling, and builds the observability that tells you an integration broke before a customer does.

Continue

Related in Hosting & Infrastructure