Home Server DR

WordPress Single-Site Recovery Runbook

Use this page when one WordPress site is broken but the server and other sites are still healthy.

Purpose

This process restores one WordPress site at a time. It is for plugin failures, theme failures, bad updates, accidental content deletion, or a compromise limited to one site.

Important: this is not the full server disaster recovery process. For primary server failure, use the Server DR runbook.

Site Structure

Each WordPress site should be isolated under its own domain directory:

/var/www/<domain>/
  public/    WordPress files
  logs/      site-specific logs
  backups/   site-specific backups
  tmp/       temporary files

Each site should also have its own Linux user, PHP-FPM pool, MariaDB database, and MariaDB user.

Result: restoring site-a.com should not roll back site-b.com or any other site.

When To Use This

Do Not Use This For

Use the Server DR runbook for those cases.

Information Needed Before Restore

Recovery Procedure

  1. Confirm the problem is limited to one site.
  2. Record the current time, domain, symptoms, and selected backup timestamp.
  3. If possible, put only the broken site into maintenance mode.
  4. Take a just-before-restore copy of the current broken files and database.
  5. Restore that site's public/ directory from the selected backup snapshot.
  6. Restore only that site's MariaDB database from the matching database dump.
  7. Confirm file ownership and permissions match the site user.
  8. Reload PHP-FPM/nginx only if configuration or permissions require it.
  9. Test the public site, /wp-admin/, login, media uploads, forms, and any business-critical workflow.
  10. Remove maintenance mode after validation.

Command Template

Use this as a step-by-step template. Replace the example values first, then run one step at a time. Stop if any verification command fails.

Step 1: Set The Site Details

Change these four values to match the broken WordPress site and the backup snapshot you want to restore.

DOMAIN="example.com"
DB_NAME="example_wp"
SITE_USER="example"
SNAPSHOT="YYYYMMDD-HHMMSS"

Step 2: Build The Paths

These paths are based on the values above. The backup path points to the files saved by the USB backup job.

SITE_ROOT="/var/www/${DOMAIN}"
WEB_ROOT="${SITE_ROOT}/public"
BACKUP_ROOT="/mnt/backup-usb/snapshots/${SNAPSHOT}/files/var/www/${DOMAIN}"
DB_BACKUP="/mnt/backup-usb/database/per-database/${SNAPSHOT}/${DB_NAME}.sql.gz"

Step 3: Verify Before Changing Anything

Confirm the live site path, backup files, and database dump exist before running any restore command.

sudo test -d "$WEB_ROOT" && echo "OK: current site path exists"
sudo test -d "$BACKUP_ROOT/public" && echo "OK: backup site files exist"
sudo test -f "$DB_BACKUP" && echo "OK: database backup exists"

Step 4: Save The Current Broken State

This gives you a local rollback point in case the selected backup snapshot is wrong.

sudo mkdir -p "${SITE_ROOT}/backups/pre-restore-${SNAPSHOT}"
sudo rsync -a --delete "$WEB_ROOT/" "${SITE_ROOT}/backups/pre-restore-${SNAPSHOT}/public/"
sudo mysqldump "$DB_NAME" | sudo gzip -c > "${SITE_ROOT}/backups/pre-restore-${SNAPSHOT}/${DB_NAME}.sql.gz"

Step 5: Restore The Site Files

This replaces only this site's WordPress files with the files from the selected backup snapshot.

sudo rsync -a --delete "$BACKUP_ROOT/public/" "$WEB_ROOT/"
sudo chown -R "${SITE_USER}:${SITE_USER}" "$WEB_ROOT"

Risk: rsync --delete removes live files that are not present in the selected backup. Confirm DOMAIN and SNAPSHOT before running this step.

Step 6: Restore The Site Database

This imports only this WordPress site's database. It does not restore every database on the server.

sudo gzip -dc "$DB_BACKUP" | sudo mysql "$DB_NAME"

Risk: restoring the database rolls this WordPress site back to the selected backup time.

Verification Commands

systemctl is-active mariadb php8.3-fpm nginx
curl -I https://example.com/
curl -I https://example.com/wp-admin/
sudo -u example wp core version --path=/var/www/example.com/public
sudo -u example wp plugin list --path=/var/www/example.com/public
sudo -u example wp theme list --path=/var/www/example.com/public

Also check the site manually in a browser: homepage, admin login, media library, forms, search, checkout, or other important pages.

Rollback If Restore Is Wrong

If the selected backup snapshot is wrong, restore from the safety copy made before the restore:

DOMAIN="example.com"
DB_NAME="example_wp"
SITE_USER="example"
RESTORE_TAG="pre-restore-YYYYMMDD-HHMMSS"
SITE_ROOT="/var/www/${DOMAIN}"
WEB_ROOT="${SITE_ROOT}/public"

sudo rsync -a --delete "${SITE_ROOT}/backups/${RESTORE_TAG}/public/" "$WEB_ROOT/"
sudo chown -R "${SITE_USER}:${SITE_USER}" "$WEB_ROOT"
sudo gzip -dc "${SITE_ROOT}/backups/${RESTORE_TAG}/${DB_NAME}.sql.gz" | sudo mysql "$DB_NAME"

Owner Explanation

Each WordPress site is treated as a separate recoverable unit. If one site breaks, we restore only that site's files and database. Other WordPress sites on the server continue running and are not rolled back.

Full server disaster recovery is only for a larger outage, such as hardware failure or a failed primary server.

Last updated: 2026-05-13