Automated Off-Site VPS Backups with Cron and Rclone
Your VPS is fine right now. That's exactly the problem — nobody sets up real backups on a healthy server, and by the time the disk fails, the provider's data center floods, or a bad rm -rf takes out /var/www, it's too late to start. If your only backup plan is a snapshot from your hosting provider, you have a single point of failure: it lives on the same infrastructure as the server it's protecting. This guide walks through setting up automated, off-site backups on a VPS using cron and rclone — free tools, a script you control, and a copy of your data that survives even if the entire VPS disappears.
Why a Snapshot Alone Isn't a Backup Strategy
A provider snapshot is great for one thing: rolling back a bad kernel update or a botched software install in minutes. But snapshots usually live in the same account, sometimes the same data center, as the VPS itself. If your account gets suspended, the provider has an outage, or you fat-finger a "delete server" button, the snapshot can go with it.
Real disaster recovery needs a copy of your data somewhere else entirely — a different provider, a different region, ideally a different company. That's what this setup gives you: your website files and database dumped, compressed, and pushed to off-site storage every night, without you touching a keyboard.
What You Need Before You Start
- SSH access to your VPS with a sudo-capable user (not logging in as
rootdirectly — see our SSH hardening guide if you haven't set that up yet) - An off-site storage destination: Backblaze B2, AWS S3, or even a Google Drive/Dropbox account with enough free space. B2 is the cheapest for pure backup storage.
- MySQL/MariaDB credentials for the database(s) you want backed up
- 10–15 minutes of downtime-free setup — none of this touches your live site
Step 1: Install Rclone
Rclone is a single binary that talks to almost every cloud storage provider using one consistent syntax. Install it with the official script:
curl https://rclone.org/install.sh | sudo bash
Confirm it installed correctly:
rclone version
Step 2: Configure a Remote
Run the interactive config wizard:
rclone config
Choose n for a new remote, name it something memorable like b2backup, then pick your provider from the list (Backblaze B2, Amazon S3, Google Drive, etc.) and follow the prompts for your access key and secret. When it's done, test the connection:
rclone lsd b2backup:
If that lists your bucket (or comes back empty with no error), the remote is working.
Step 3: Write the Backup Script
Create a script that dumps your database, archives your site files, and pushes both off-site. Save this as /root/scripts/backup.sh:
#!/bin/bash
set -e
DATE=$(date +%F)
BACKUP_DIR="/root/backups/$DATE"
SITE_DIR="/var/www/html"
DB_NAME="your_database"
DB_USER="your_db_user"
DB_PASS="your_db_password"
REMOTE="b2backup:my-site-backups"
mkdir -p "$BACKUP_DIR"
# Dump the database
mysqldump -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" | gzip > "$BACKUP_DIR/db.sql.gz"
# Archive site files
tar -czf "$BACKUP_DIR/files.tar.gz" -C "$SITE_DIR" .
# Push to off-site storage
rclone copy "$BACKUP_DIR" "$REMOTE/$DATE" --transfers=4
# Clean up local copy older than 3 days to save disk space
find /root/backups/* -maxdepth 0 -type d -mtime +3 -exec rm -rf {} \;
# Delete remote backups older than 30 days
rclone delete --min-age 30d "$REMOTE"
Make it executable:
chmod +x /root/scripts/backup.sh
Never hardcode the DB password in a world-readable file. Lock the script down with chmod 700 and keep it under /root, or better, read credentials from a separate .my.cnf file mysqldump picks up automatically.
Step 4: Schedule It with Cron
Open the root crontab:
sudo crontab -e
Add a line to run the backup every night at 2 AM server time:
0 2 * * * /root/scripts/backup.sh >> /var/log/backup.log 2>&1
The >> /var/log/backup.log 2>&1 part matters — without it, cron silently swallows both the output and any errors, and you won't know the backup stopped working until you actually need it.
Cron Schedule Reference
| Schedule | Cron Expression | Good For |
|---|---|---|
| Nightly | 0 2 * * * | Most WordPress/small business sites |
| Every 6 hours | 0 */6 * * * | Active WooCommerce stores, frequent content changes |
| Weekly | 0 3 * * 0 | Mostly-static sites, low write volume |
Step 5: Actually Test the Restore
This is the step almost everyone skips, and it's the one that matters most. A backup you haven't restored is a guess, not a backup. Pick a quiet moment and run through it once:
- Pull a backup down:
rclone copy b2backup:my-site-backups/2026-07-14 /tmp/restore-test - Spin up a throwaway database and import the dump:
gunzip -c db.sql.gz | mysql -u root -p test_restore_db - Extract the files archive to a scratch directory and spot-check that
wp-config.php, uploads, and plugin folders are all there - Time how long the whole process took — that number is your real recovery time, not a guess
Do this test once after setup and then again every few months. Storage providers change APIs, disks fill up, and scripts silently break — you want to find that out on a Tuesday afternoon, not during an actual outage.
Prevention: Keep the Backup Pipeline Honest
- Alert on failure, not just success. Add a simple check at the end of the script that pings a monitoring service (like a healthchecks.io URL) only if the previous commands succeeded — you'll get notified the moment a night's backup silently fails.
- Watch disk usage. Local temp copies before the off-site push can fill the disk fast on a busy store. The cleanup line in the script above handles this, but check
df -hoccasionally. - Keep at least one backup outside your Getwebup account entirely. If the goal is surviving a worst-case scenario, the backup can't live in the same blast radius as the thing it protects.
- Encrypt sensitive dumps. If your database contains customer data, pipe the dump through
gpgbefore it leaves the server, or use rclone's built-in crypt remote.
Snapshots vs. This Setup
These two aren't competing — they cover different failure modes. Keep both:
| Provider Snapshot | Cron + Rclone Off-Site Backup | |
|---|---|---|
| Speed to restore | Minutes, whole server | Slower, file/database level |
| Survives account/provider loss | No | Yes |
| Good for | Undoing a bad update or config change | Disaster recovery, migration, ransomware/hack recovery |
| Granularity | Full disk only | Pick specific files or database tables to restore |
Need this done for you instead of scripting it yourself? Getwebup's managed VPS plans include off-site backup configuration as part of onboarding — open a ticket and we'll set the whole pipeline up on your server.
Frequently asked questions
Is a provider snapshot enough, or do I still need off-site backups?
A snapshot is great for quickly undoing a bad update, but it usually lives in the same account and data center as your VPS. If your account is suspended or the provider has an outage, the snapshot can go with it. Off-site backups protect against that scenario, so it's worth running both.
How much does off-site storage like Backblaze B2 cost for a typical site?
For a small to mid-size WordPress or WooCommerce site, database and file backups usually total a few gigabytes per snapshot. Backblaze B2 charges roughly $6 per TB per month, so most sites with a 30-day retention window pay well under a dollar a month.
Why did my cron backup job silently stop running?
The most common cause is the script failing partway through without cron logging it anywhere — always redirect output to a log file with >> /var/log/backup.log 2>&1 in the crontab line. Also check that the mysqldump credentials haven't expired and that the remote storage token hasn't been revoked.
Can I back up multiple databases or multiple sites with one script?
Yes. Loop over an array of database names and site directories inside the script, dumping each to its own dated subfolder before the rclone copy step. Just make sure your remote destination path includes the site name so backups from different sites don't overwrite each other.
How do I restore from one of these backups if my VPS is completely gone?
Spin up a fresh VPS, install rclone, pull the latest backup folder down with rclone copy, then import the SQL dump with mysql and extract the files archive into your web root. Update DNS if the IP changed, and you're back online without needing the original server at all.