
Your backup is not your recovery plan. A disaster recovery tutorial should end with steps you can follow under stress. You need to know where backups live, how long restores take, how DNS flips, and how you’ll verify the site is truly back.
This guide builds a practical DR plan for a common VPS/dedicated stack: Nginx/Apache, PHP/WordPress, plus DNS and mail gotchas.
You’ll stand up a small “recovery sandbox” VPS. Then you’ll restore into it, validate the app, and rehearse a controlled cutover.
What you’re building (and what it protects you from)
DR mostly comes down to two numbers:
- RPO (Recovery Point Objective): how much data you can afford to lose (e.g., 15 minutes).
- RTO (Recovery Time Objective): how long you can be down (e.g., 60 minutes).
This tutorial targets a realistic small-business goal for 2026 VPS hosting.
Use hourly (or 15‑minute) incrementals where they matter. Pair that with a restore process you can time and repeat.
Incidents this plan is designed to handle:
- Bad OS update or package conflict that breaks Nginx/Apache/PHP
- Disk corruption, filesystem damage, or a failed volume
- Compromised site that requires a clean rebuild + selective restore
- Human error: deleted vhost, wiped uploads, wrong firewall rule
Prerequisites and scope (keep it tight)
You’ll need:
- A Linux hosting server (Ubuntu 24.04 LTS or Debian 12 are common in 2026)
- SSH access as root or a sudo user
- A place to store offsite backups (object storage, a second VPS, or managed backup storage)
- Control over DNS for your domain (at your registrar or DNS provider)
If you’re running cPanel/WHM, the same DR concepts apply. The restore steps in this guide won’t match, though.
Pair your DR runbook with your WHM backup strategy and regular restore drills.
Step 1: Decide your “golden restore” target
When production is already broken, restoring onto the same machine often wastes time.
A cleaner pattern is simple. Restore onto a fresh server, validate it, then cut over.
Create (or reserve) a small standby instance:
- Warm standby (recommended): a minimal VPS you patch weekly, use for restore tests, and keep ready for an emergency cutover.
- Cold standby: no server until you need one. Cheaper, but provisioning time becomes part of your RTO.
If you want a standby VPS that’s ready for recovery drills without paying dedicated-server prices, start with a HostMyCode VPS sized to boot, restore, and carry traffic temporarily.
If you’d rather not handle as much day-to-day ops, managed VPS hosting is a better fit for teams that want help during incidents.
Step 2: Inventory your hosting stack (don’t guess later)
Write down what you’ll actually need to recover.
On a WordPress VPS, a bare-minimum inventory usually includes:
- Web root(s):
/var/wwwor/home/<user>/public_html - Nginx/Apache config:
/etc/nginx,/etc/apache2 - PHP config pools:
/etc/php/8.3/fpm/pool.d(or your PHP version) - Database dumps (even if you don’t restore the whole DB service): WordPress
wp_optionsandwp_postsmatter most - SSL material: Let’s Encrypt typically in
/etc/letsencrypt - Crons/timers:
/etc/cron.*, user crontabs, or systemd timers - Firewall rules: nftables/iptables config, and cloud/VPS security groups
Quick command set to capture this inventory:
sudo hostnamectl
sudo uname -a
sudo nginx -v 2>/dev/null || true
sudo apache2 -v 2>/dev/null || true
php -v 2>/dev/null || true
sudo systemctl list-units --type=service --state=running
sudo ss -lntup
sudo df -hT
sudo lsblk
Save the output somewhere secure (private repo, vault, or a locked-down note).
During an outage, this becomes your map.
Step 3: Build an offsite backup set you can actually restore
For DR, backups must be offsite and independent.
Snapshots are great for quick rollbacks. They do not cover the “the server is gone” scenario.
A solid 2026 baseline for most hosting VPS workloads:
- Daily full backup + hourly incrementals (or every 15 minutes for high-write sites)
- 30 days retention for daily points, plus 3–6 months monthly archives
- Encrypted at rest and encrypted in transit
- Restore drill at least monthly
If you want a concrete restore-testing workflow, see VPS backup verification tutorial for ideas on automated restore checks.
Step 4: Prepare DNS for fast, safe cutovers (TTL and records)
DNS is the reason many restores feel slow.
If your A/AAAA records have a high TTL, clients will keep hitting the old IP after you “switched.”
Do this before you need it:
- Set TTL to 300 seconds (5 minutes) for the main
A/AAAArecords. - Record your current values and keep them in your runbook.
- Make sure registrar login and DNS access are part of your incident access checklist.
If mail lives on the same server, treat DNS changes with extra care.
Keep MX, SPF, DKIM, and DMARC documented. Avoid “panic edits.”
For mail-specific DNS, MX record setup tutorial is a useful reference.
Step 5: Create a recovery sandbox VPS
Provision a clean VPS that matches production as closely as you can.
If production is Ubuntu 24.04 with Nginx and PHP-FPM, test on the same OS family and package set.
On the sandbox server:
sudo apt update && sudo apt -y upgrade
sudo apt -y install rsync curl unzip tar jq
Install your web stack. Example for Nginx + PHP-FPM (adjust PHP version to match your app):
sudo apt -y install nginx php8.3-fpm php8.3-cli php8.3-mysql php8.3-xml php8.3-curl php8.3-gd php8.3-mbstring
Keep it boring and predictable.
DR is not the time to change architectures.
Step 6: Restore the site files (and keep permissions sane)
Most restore failures aren’t caused by missing data.
They come from basics: ownership, paths, symlinks, and permissions.
Example: restoring a WordPress site stored at /var/www/example.com from a tarball:
sudo mkdir -p /var/www/example.com
sudo tar -xpf /backups/example.com-files.tar -C /var/www/example.com
sudo chown -R www-data:www-data /var/www/example.com
If your backups are rsync-based (common for incrementals), restore with:
sudo rsync -aHAX --numeric-ids /backups/example.com/ /var/www/example.com/
Pitfall: don’t dump a potentially compromised backup on top of a clean rebuild without checking it.
If you suspect malware, isolate the restore and inspect first (especially wp-content/plugins and uploads).
Step 7: Restore the database safely (then fix site URLs)
Even if your database runs on a separate host, your runbook still needs DB restore steps.
For a typical WordPress DB dump:
sudo apt -y install mariadb-server
sudo systemctl enable --now mariadb
sudo mysql -e "CREATE DATABASE wpdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql -e "CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'STRONG_PASSWORD';"
sudo mysql -e "GRANT ALL PRIVILEGES ON wpdb.* TO 'wpuser'@'localhost'; FLUSH PRIVILEGES;"
Restore:
mysql -u wpuser -p wpdb < /backups/wpdb.sql
If you’re restoring to a temporary hostname (recommended for validation), update URLs using WP-CLI:
cd /var/www/example.com
sudo -u www-data wp search-replace 'https://example.com' 'https://recovery.example.net' --all-tables
If you don’t have WP-CLI installed:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
sudo chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
Step 8: Bring up the web server and confirm the site is healthy
Create a minimal Nginx server block for the recovery hostname.
Example at /etc/nginx/sites-available/example.com:
server {
listen 80;
server_name recovery.example.net;
root /var/www/example.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
Enable and reload:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
sudo nginx -t
sudo systemctl reload nginx
Basic health checks:
- HTTP status:
curl -I http://recovery.example.net - PHP running: confirm no 502 errors; check
journalctl -u php8.3-fpm -n 100 - Nginx errors:
sudo tail -n 200 /var/log/nginx/error.log
If you need HTTPS on the recovery hostname, use a separate certificate.
Or keep recovery validation on HTTP and enable HTTPS right before cutover.
For common HTTPS mistakes and renewals, keep this reference handy: SSL certificate setup guide.
Step 9: Restore scheduled tasks (cron and systemd timers)
Your site can look “fine” while background work is stalled.
Restore anything that runs scheduled tasks: crontabs, systemd timers, queue workers.
List crons:
sudo ls -la /etc/cron.*
sudo crontab -l || true
sudo ls -la /var/spool/cron/crontabs 2>/dev/null || true
If your WordPress relies on real cron (recommended for reliability), confirm it’s present on the recovery server.
This pairs well with: WordPress Cron Job tutorial.
Step 10: Plan the cutover (DNS, SSL, and caching)
Cutover is where small mistakes become long outages.
Treat it like a short, controlled change window.
Cutover checklist (15–30 minutes):
- Confirm backup freshness (timestamp of latest successful run).
- Put the old site into maintenance mode (or at least disable writes) to avoid split-brain content.
- Take one last database dump from production (even if the server is limping).
- Restore final delta to the recovery server (files + DB).
- Install/enable production SSL on the recovery server (if required).
- Update DNS A/AAAA to the recovery server IP.
- Purge CDN caches (if used) and confirm origin health.
Practical DNS tip: if you can’t wait for global propagation, validate with a local hosts-file override before flipping public DNS.
On your laptop:
sudo nano /etc/hosts
# Add:
203.0.113.50 example.com www.example.com
Now you can test against the new server without sending real users there.
Step 11: Post-cutover verification (the part most teams skip)
Once DNS changes go out, don’t stop at “homepage loads.”
Check the workflows people actually use.
- Login: admin login works, no redirect loops
- Forms/checkout: submit test contact form or a $1 test product (for WooCommerce)
- Uploads: media upload and thumbnail generation
- Email: password reset email arrives; SPF/DKIM still pass if you changed IPs
- Performance: TTFB isn’t terrible; watch CPU and memory for 10–15 minutes
Run these quick commands on the new server during the first hour:
sudo uptime
sudo free -h
sudo ss -s
sudo tail -n 200 /var/log/nginx/error.log
sudo journalctl -p warning -n 200
If the server drags after the restore, you’re usually looking at resource limits, disk I/O, or too many PHP workers.
This troubleshooting guide helps you narrow it down quickly: VPS performance troubleshooting tutorial.
Step 12: Write the runbook (copy/paste friendly)
Your DR plan should live as a runbook a tired human can follow at 3 a.m.
Store it somewhere secure. Keep access tightly controlled.
Runbook template:
- Contacts: registrar/DNS provider, hosting provider, on-call list
- Credentials: where stored, who can access, break-glass process
- Infrastructure: server sizes, OS versions, IPs, firewall ports
- Backup sources: locations, encryption keys, retention
- Restore steps: files, database, configs, permissions
- Validation: URLs to test, commands to run, expected outputs
- Cutover: DNS records to change, TTLs, rollback plan
- Post-cutover: monitoring checks, log checks, stakeholder updates
Rollback plan matters: document how to point DNS back to the old IP.
Also document how long you’ll wait before calling it stable.
Hardening notes for DR environments (don’t restore into an open server)
A recovery server still needs basic security controls.
Minimum baseline:
- SSH keys only for admin access
- Firewall allows only required ports (80/443, SSH from trusted IPs)
- Automatic security updates or at least weekly patching
- Fail2Ban or equivalent brute-force controls if exposed publicly
If the incident looks security-related, assume credentials are burned.
Rotate SSH keys and passwords as part of the recovery flow.
When to step up from VPS to dedicated (DR reality check)
If your restore tests can’t hit your RTO on a small VM, believe the test.
DR drills expose sizing issues that are easy to ignore on normal days.
- CPU pegged during restore + traffic: more cores help, but also consider caching and PHP-FPM tuning.
- Disk restore takes forever: you may need faster storage (NVMe) or a different backup format.
- Memory pressure: DB and PHP fight for RAM during recovery.
For steady high traffic or strict RTO targets, a dedicated server plus a smaller warm-standby VPS is often simpler than trying to make one mid-sized VPS do everything.
Summary: a DR plan you can execute
A good DR plan is intentionally boring.
Restore onto a fresh host. Validate with a checklist. Then cut over with a low DNS TTL.
The payoff is certainty. You’ll know how long recovery takes because you’ve already timed it.
If you’re building your DR process around predictable infrastructure, start with a HostMyCode VPS for recovery drills and a warm standby.
If you want a team that can help keep systems patched and respond faster during incidents, consider managed VPS hosting from HostMyCode.
If recovery time matters, practice restores on infrastructure that closely matches production. A HostMyCode VPS gives you a clean place to run monthly DR drills without touching your live server. If you’d rather have help with patching, monitoring, and incident response, managed VPS hosting can reduce the operational risk.
FAQ
How often should I run a restore drill?
At least monthly, and after any major change (PHP upgrade, web server switch, new plugin stack).
If your site is revenue-critical, run a drill quarterly at minimum and validate checkout/forms.
Is a VPS snapshot the same as disaster recovery?
No. Snapshots help you roll back quickly on the same platform, but they don’t replace offsite backups or a tested cutover plan.
DR assumes the original server may be gone.
What DNS TTL should I use for a DR-friendly setup?
For the main A/AAAA records, 300 seconds is a practical default.
Don’t drop TTL to very low values for everything; keep it focused on records you’ll change during cutover.
What’s the fastest way to validate a cutover before changing DNS?
Use a hosts-file override to point your domain to the recovery server IP from your own machine.
Validate admin login, forms, uploads, and key pages before you flip public DNS.
What should I do first if I suspect the server was compromised?
Stop the bleed: restrict access, preserve logs, and avoid restoring blindly over the top.
Rebuild a clean server, restore only known-good data, and rotate credentials (SSH keys, admin passwords, API keys).