The 3-2-1 Strategy
The backup rule: 3 copies of the data, on 2 different media, and 1 copy offsite (cloud, another server).
UpdraftPlus: Setup
Install the UpdraftPlus plugin and go to Settings → UpdraftPlus:
| Setting | Recommended value |
|---|---|
| File backup schedule | Daily |
| Retain this many backups | 7 |
| Database backup schedule | Every 4 hours |
| Remote storage | Google Drive / S3 / Dropbox |
Manual Backup from the CLI
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M)
SITE_DIR=/var/www/mysite
BACKUP_DIR=/backups/wordpress
# Database backup
mysqldump -u wpuser -p wppassword wpdb > $BACKUP_DIR/db_$DATE.sql
# Files backup
tar -czf $BACKUP_DIR/files_$DATE.tar.gz $SITE_DIR/wp-content/
# Delete old backups (older than 30 days)
find $BACKUP_DIR -mtime +30 -delete
Adding It to cron
crontab -e
# Daily at 3:00
0 3 * * * /usr/local/bin/wp-backup.sh >> /var/log/wp-backup.log 2>&1
Common backup and restore errors
A backup is useless if it cannot be restored. Testing a backup usually gets postponed until the first real incident — and that is exactly when errors that could have been caught earlier show up. Here are common errors when creating and restoring a backup.
| Error text or symptom | Cause | What to do |
|---|---|---|
| Error establishing a database connection after restoring a dump | wp-config.php still has the old DB_NAME, DB_USER and DB_HOST from the original server | Set the constants for the new database before the first launch of the site |
| mysqldump: Got error: 1044 access denied when using LOCK TABLES | The database user lacks the LOCK TABLES privilege needed for a consistent dump | Grant LOCK TABLES or run mysqldump with the --single-transaction flag |
| White screen after restoring without wp-content/uploads | The backup covered only the database, not the files — the media library is physically missing | Always back up the database and wp-content together, not separately |
| tar: Unexpected EOF in archive | The backup archive is corrupted due to a dropped connection or low disk space during creation | Check df -h before the backup and verify the archive checksum afterward |
For the correct way to run mysqldump and restore, see the separate article on mysqldump and restore.
When UpdraftPlus and a bash script are not enough
- A database larger than 5–10 GB — mysqldump without --single-transaction locks InnoDB tables for a long time; you need a hot physical copy tool instead
- Sites with changes every minute (a forum, a marketplace) — a backup every 4 hours loses data between copies; you need replication or MySQL binary logs
- Hosting without cron or SSH access — there is nowhere to run a bash script; only a plugin or the control panel's own backup remains an option
- A WordPress multisite network with dozens of subdomains — UpdraftPlus backs up the whole network; restoring a single site from it is not possible
- A legal requirement to keep backups only inside a specific country — a plugin's cloud storage does not always let you choose the exact data center
Content change frequency also matters separately. An online store with dozens of orders per hour loses data at any backup interval longer than an hour — for such projects the database backup needs to track peak activity, not a convenient time of day.
Encryption and backup integrity
A backup uploaded to public cloud storage without encryption is a leak source no better than a site hack. Add encryption and an integrity check directly to the script.
openssl enc -aes-256-cbc -salt -pbkdf2 \
-in $BACKUP_DIR/db_$DATE.sql \
-out $BACKUP_DIR/db_$DATE.sql.enc \
-pass file:/root/.backup_key
sha256sum $BACKUP_DIR/db_$DATE.sql.enc > $BACKUP_DIR/db_$DATE.sql.enc.sha256
For more on hardening access to backups and the server, see the article on WordPress security hardening.
How to check that a backup actually works
- Restore the dump on a test server: the command should finish with no errors and no skipped tables
- Compare the archive checksum against the one saved at creation time — a mismatch means a corrupted file
- Open the site on a staging copy and confirm that all posts and media files are in place
- Check that the job actually runs on schedule, not just that it is listed in crontab
# Restore and checksum verification
sha256sum -c db_$DATE.sql.enc.sha256
mysql -u user -p test_db < db_$DATE.sql
# Check that cron actually ran
grep wp-backup /var/log/syslog | tail -5
To test a restore without risking the live site, use a separate staging environment.