On a VPS, you don’t need a plugin to back up WordPress. A short shell script can dump the database, archive the files, keep a rotation, and send copies off-site, running from cron outside PHP. That makes it faster, more reliable on large sites, and it works even when WordPress itself is broken.
Prefer a plugin? See WordPress backup plugins compared.
What to back up
| Part | Where | Why |
|---|---|---|
| Database | MySQL/MariaDB | Posts, pages, settings, users, orders |
wp-content/ |
Site directory | Uploads, themes, plugins |
wp-config.php |
Site root | Database credentials, salts, settings |
| Server config (optional) | /etc/nginx/sites-available/…, PHP pool |
Faster rebuilds |
WordPress core files can be downloaded again, but backing up the whole site directory is simple and small enough for most sites.
1. Dump the database
With WP-CLI (reads credentials from wp-config.php):
cd /var/www/example.com
wp db export /backups/example-db-$(date +%F).sql --allow-root
Or with mysqldump:
mysqldump --single-transaction --quick --no-tablespaces \
-u wpuser -p'secret' wordpress | gzip > /backups/example-db-$(date +%F).sql.gz
--single-transaction gives a consistent snapshot of InnoDB tables without locking the site. Avoid passwords on the command line in scripts: use a ~/.my.cnf file with chmod 600 instead. See mysqldump and WP-CLI guide.
2. Archive the files
tar --exclude='wp-content/cache' --exclude='wp-content/updraft' \
-czf /backups/example-files-$(date +%F).tar.gz -C /var/www example.com
3. A complete backup script
#!/bin/bash
# /usr/local/bin/wp-backup.sh
set -euo pipefail
SITE=example.com
WEBROOT=/var/www/$SITE
DEST=/backups/$SITE
KEEP_DAYS=14
STAMP=$(date +%F-%H%M)
mkdir -p "$DEST"
cd "$WEBROOT"
# database (credentials from wp-config.php via WP-CLI)
sudo -u www-data wp db export - | gzip > "$DEST/db-$STAMP.sql.gz"
# files
tar --exclude='wp-content/cache' -czf "$DEST/files-$STAMP.tar.gz" -C /var/www "$SITE"
# local retention
find "$DEST" -type f -mtime +$KEEP_DAYS -delete
echo "Backup done: $STAMP"
sudo chmod 700 /usr/local/bin/wp-backup.sh
sudo mkdir -p /backups && sudo chmod 700 /backups
4. Schedule it with cron
sudo crontab -e
30 3 * * * /usr/local/bin/wp-backup.sh >> /var/log/wp-backup.log 2>&1
See crontab guide.
5. Send it off-site
A backup on the same VPS isn’t enough. Options:
- rsync over SSH to another server (for example a storage VPS):
rsync -az --delete /backups/ [email protected]:/srv/backups/web1/
- rclone to S3-compatible storage (Backblaze B2, Wasabi, AWS S3, MinIO):
rclone copy /backups remote:my-bucket/web1 --min-age 1m
- restic or Borg: deduplicated, encrypted, versioned backups. That’s ideal for keeping months of history cheaply.
Add the off-site step to the end of the script. See rsync examples, off-site VPS backups, and storage VPS.
6. Restore
# files
sudo tar -xzf files-2026-10-01-0330.tar.gz -C /var/www
sudo chown -R www-data:www-data /var/www/example.com
# database
gunzip -c db-2026-10-01-0330.sql.gz | sudo -u www-data wp db import - --path=/var/www/example.com
If you restore to a different domain, run a search-replace afterwards. See change the WordPress site URL.
Test a restore on a staging site every few months. See WordPress staging site.
Monitoring
- Check the log, or send yourself a message on failure (
|| curlto a webhook, or a healthcheck service such as Healthchecks.io or Uptime Kuma push monitors). See Uptime Kuma. - Watch disk space in
/backups. See disk full troubleshooting.
Quick reference
| Task | Command |
|---|---|
| DB dump (WP-CLI) | wp db export - | gzip > db.sql.gz |
| DB dump (mysqldump) | mysqldump --single-transaction db | gzip |
| Files | tar -czf files.tar.gz -C /var/www example.com |
| Schedule | cron, 30 3 * * * |
| Off-site | rsync / rclone / restic |
| Restore DB | gunzip -c db.sql.gz | wp db import - |
Related: WordPress backup plugins and WordPress VPS hosting.