How to Set Up Automated Backups on Your VPS Using Rsync and Cron

How to Set Up Automated Backups on Your VPS Using Rsync and Cron

Your VPS is only as reliable as your last backup. Hardware fails, misconfigurations happen, scripts go wrong — and when they do, a recent backup is the difference between a 10-minute recovery and a total data loss disaster. Yet most VPS users either back up manually (and forget) or don’t back up at all.

In this guide, you’ll set up a fully automated VPS backup system using Rsync and Cron — no third-party tools, no monthly fees, no complexity. By the end, your files, databases, and configs will be backed up on a schedule you control, stored wherever you choose.

Why Rsync and Cron?

There are dozens of backup tools available, but Rsync and Cron are the standard choice for Linux VPS backups for good reasons:

  • Rsync — Only transfers files that have changed (incremental), making backups fast and bandwidth-efficient. It’s pre-installed on virtually every Linux system.
  • Cron — The Linux task scheduler. Set it once, and it runs your backup script automatically at whatever interval you choose — hourly, daily, weekly.
  • No dependencies — No commercial software, no agents, no accounts required.
  • Flexible destination — Back up to a second VPS, a local machine, an external drive, or cloud storage (S3-compatible services).

What Should You Back Up?

A complete VPS backup strategy covers three categories:

Category What to include Example paths
Web files Website content, uploads, themes /var/www/
Databases MySQL/MariaDB or PostgreSQL dumps Exported to /var/backups/db/
Config files Nginx, SSH, PHP, app configs /etc/nginx/, /etc/ssh/

💡 VPS.DO Tip: For critical production servers, follow the 3-2-1 backup rule: 3 copies of your data, on 2 different media types, with 1 copy offsite. For example: your VPS (original), a second VPS backup (remote copy), and local storage.

Requirements

  • Ubuntu VPS 22.04 or 24.04 LTS (source server)
  • A backup destination: a second VPS, a remote server, or local machine with SSH access
  • Root or sudo access
  • Rsync installed (pre-installed on Ubuntu — verify with rsync --version)

Part 1: Set Up Passwordless SSH Authentication

For automated backups to run without human input, your source VPS needs to connect to the backup destination without a password prompt. This is done with SSH key pairs.

Step 1: Generate an SSH Key on the Source VPS

ssh-keygen -t ed25519 -C "vps-backup-key" -f ~/.ssh/backup_key -N ""

This creates two files:

  • ~/.ssh/backup_key — Private key (stays on your source VPS)
  • ~/.ssh/backup_key.pub — Public key (copied to the destination)

Step 2: Copy the Public Key to the Backup Destination

ssh-copy-id -i ~/.ssh/backup_key.pub user@BACKUP_SERVER_IP

Or manually append it to the destination’s authorized_keys:

cat ~/.ssh/backup_key.pub | ssh user@BACKUP_SERVER_IP "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

Step 3: Test the Passwordless Connection

ssh -i ~/.ssh/backup_key user@BACKUP_SERVER_IP

If you connect without a password prompt, the key setup is complete. ✅


Part 2: Create the Backup Script

Step 1: Create a Backup Directory Structure

On your source VPS, create a directory for local backup logs and temporary database dumps:

sudo mkdir -p /var/backups/{db,logs}
sudo chown -R $USER:$USER /var/backups

Step 2: Write the Main Backup Script

nano ~/backup.sh

Paste the following complete backup script. Edit the variables at the top to match your setup:

#!/bin/bash

# ============================================
# VPS Automated Backup Script
# Source: your VPS | Destination: remote server
# ============================================

# --- CONFIGURATION ---
BACKUP_DEST_USER="backupuser"
BACKUP_DEST_IP="YOUR_BACKUP_SERVER_IP"
BACKUP_DEST_PATH="/home/backupuser/vps-backups"
SSH_KEY="$HOME/.ssh/backup_key"
DATE=$(date +%Y-%m-%d)
LOG_FILE="/var/backups/logs/backup-$DATE.log"

# Directories to back up
WEB_DIR="/var/www"
CONFIG_DIRS="/etc/nginx /etc/ssh /etc/php"

# Database credentials
DB_USER="root"
DB_PASS="YOUR_DB_PASSWORD"
DB_BACKUP_PATH="/var/backups/db"

# --- FUNCTIONS ---
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# --- START ---
log "===== Backup started ====="

# Step 1: Dump all MySQL databases
log "Dumping MySQL databases..."
mysqldump -u "$DB_USER" -p"$DB_PASS" --all-databases \
  --single-transaction --quick \
  > "$DB_BACKUP_PATH/all-databases-$DATE.sql" 2>> "$LOG_FILE"

if [ $? -eq 0 ]; then
    log "Database dump successful."
else
    log "ERROR: Database dump failed!"
fi

# Step 2: Compress database dump
gzip -f "$DB_BACKUP_PATH/all-databases-$DATE.sql"
log "Database dump compressed."

# Step 3: Rsync web files to remote server
log "Syncing web files..."
rsync -avz --delete \
  -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \
  "$WEB_DIR/" \
  "$BACKUP_DEST_USER@$BACKUP_DEST_IP:$BACKUP_DEST_PATH/www/" \
  >> "$LOG_FILE" 2>&1

if [ $? -eq 0 ]; then
    log "Web files synced successfully."
else
    log "ERROR: Web file sync failed!"
fi

# Step 4: Rsync config files to remote server
log "Syncing config files..."
rsync -avz --delete \
  -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \
  $CONFIG_DIRS \
  "$BACKUP_DEST_USER@$BACKUP_DEST_IP:$BACKUP_DEST_PATH/configs/" \
  >> "$LOG_FILE" 2>&1

# Step 5: Rsync database dumps to remote server
log "Syncing database dumps..."
rsync -avz \
  -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \
  "$DB_BACKUP_PATH/" \
  "$BACKUP_DEST_USER@$BACKUP_DEST_IP:$BACKUP_DEST_PATH/databases/" \
  >> "$LOG_FILE" 2>&1

# Step 6: Clean up old local database dumps (keep last 7 days)
find "$DB_BACKUP_PATH" -name "*.sql.gz" -mtime +7 -delete
log "Old local database dumps cleaned up."

log "===== Backup finished ====="

Step 3: Make the Script Executable

chmod +x ~/backup.sh

Step 4: Test the Script Manually

Run it once by hand before automating, so you can catch any errors:

~/backup.sh

Check the log file to confirm everything succeeded:

cat /var/backups/logs/backup-$(date +%Y-%m-%d).log

Also verify files arrived on the backup destination:

ssh -i ~/.ssh/backup_key user@BACKUP_SERVER_IP "ls -lh ~/vps-backups/"

Part 3: Automate with Cron

Now that the script works, schedule it to run automatically using Cron.

Step 1: Open the Crontab Editor

crontab -e

Step 2: Add Your Backup Schedule

Choose the schedule that fits your needs:

# Run daily backup at 2:00 AM
0 2 * * * /bin/bash /root/backup.sh

# Run every 6 hours
0 */6 * * * /bin/bash /root/backup.sh

# Run weekly on Sunday at 3:00 AM
0 3 * * 0 /bin/bash /root/backup.sh

For most VPS setups, a daily backup at 2–3 AM is the sweet spot — enough protection without excessive storage consumption.

Understanding Cron Syntax

# ┌──── minute (0–59)
# │ ┌──── hour (0–23)
# │ │ ┌──── day of month (1–31)
# │ │ │ ┌──── month (1–12)
# │ │ │ │ ┌──── day of week (0–7, 0 and 7 = Sunday)
# │ │ │ │ │
# * * * * * command

Need help building cron expressions? Use crontab.guru — paste your expression and it tells you exactly when it will run in plain English.

Step 3: Verify Cron Is Running

sudo systemctl status cron

Should show active (running). ✅


Part 4: Backup Rotation — Managing Storage

Without cleanup, backups accumulate indefinitely and fill your disk. Implement rotation to keep only the most recent copies.

Keep the Last N Days of Backups

Add these lines to your backup script or as a separate cron job:

# Delete log files older than 30 days
find /var/backups/logs -name "*.log" -mtime +30 -delete

# Delete database dumps older than 14 days
find /var/backups/db -name "*.sql.gz" -mtime +14 -delete

Remote Backup Rotation

On the backup destination server, add a cron job to clean old backups:

# Run on backup destination server
0 4 * * * find /home/backupuser/vps-backups/databases -name "*.sql.gz" -mtime +30 -delete

Part 5: Backing Up MySQL/MariaDB Databases Only

If you only need to back up databases (common for WordPress or app servers), use this lightweight script:

#!/bin/bash

DB_USER="root"
DB_PASS="YOUR_PASSWORD"
BACKUP_DIR="/var/backups/db"
DATE=$(date +%Y-%m-%d_%H-%M)

mkdir -p "$BACKUP_DIR"

# Loop through all databases and dump each one separately
for DB in $(mysql -u"$DB_USER" -p"$DB_PASS" -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|sys)"); do
    mysqldump -u"$DB_USER" -p"$DB_PASS" --single-transaction "$DB" \
      | gzip > "$BACKUP_DIR/${DB}_${DATE}.sql.gz"
    echo "Backed up: $DB"
done

echo "All databases backed up to $BACKUP_DIR"

This creates individual compressed files per database — making it easy to restore a single database without extracting a massive all-databases dump.


Part 6: Verify Your Backups Regularly

A backup you’ve never tested is a backup you can’t trust. Schedule a monthly restore test:

Test File Restoration

# Copy a specific file from backup to a test location
rsync -avz -e "ssh -i ~/.ssh/backup_key" \
  user@BACKUP_SERVER_IP:~/vps-backups/www/site1.com/ \
  /tmp/restore-test/

Test Database Restoration

# Download the dump from the backup server
scp -i ~/.ssh/backup_key user@BACKUP_SERVER_IP:~/vps-backups/databases/mydb_2025-01-01.sql.gz /tmp/

# Decompress and restore to a test database
gunzip /tmp/mydb_2025-01-01.sql.gz
mysql -u root -p test_restore_db < /tmp/mydb_2025-01-01.sql

Optional: Email Notifications for Backup Status

Get notified when backups succeed or fail. Install a lightweight mail tool:

sudo apt install mailutils -y

Add this to the end of your backup script:

# Send success/failure email
if grep -q "ERROR" "$LOG_FILE"; then
    mail -s "⚠️ VPS Backup FAILED - $DATE" you@youremail.com < "$LOG_FILE"
else
    mail -s "✅ VPS Backup Successful - $DATE" you@youremail.com < "$LOG_FILE"
fi

Backup Storage Options for Your VPS

Where you store your backups matters. Here are the most common options:

Option Cost Best for
Second VPS (e.g., another VPS.DO VPS) ~$5–20/month Full offsite backup with SSH access
Local workstation Free Development servers, personal projects
S3-compatible object storage (Backblaze B2, Wasabi) ~$0.006/GB/month Cost-effective offsite storage at scale
External NAS/hard drive One-time cost Home labs, physical backup

For most VPS.DO customers, the simplest approach is to use a second low-cost VPS as the backup destination — giving you full SSH control, no egress fees, and geographic separation between your production and backup servers.


Quick Troubleshooting

Cron job not running

Check cron logs to confirm the job is being triggered:

grep CRON /var/log/syslog | tail -20

Rsync permission denied

The SSH key path in the script must match exactly. Confirm with:

ls -la ~/.ssh/backup_key

Also ensure the backup destination’s ~/.ssh/authorized_keys has correct permissions (chmod 600).

MySQL dump fails with access denied

Store credentials in a secure ~/.my.cnf file instead of the script:

nano ~/.my.cnf
[mysqldump]
user=root
password=YOUR_PASSWORD
chmod 600 ~/.my.cnf

Then remove the -u and -p flags from your mysqldump command — it will read credentials from the file automatically.


Final Thoughts

Setting up automated backups is one of the highest-ROI tasks you can do on a VPS. It takes about an hour to configure properly, and once it’s running, you can recover from almost any disaster in minutes rather than starting from scratch.

The Rsync + Cron combination is battle-tested, requires no third-party dependencies, and scales from a single personal site to a server hosting dozens of production applications. Set it up today — and test your restore process before you ever need it.

Running your backups on VPS.DO? Our KVM VPS plans give you full root access, high-speed SSD storage, and 1 Gbps ports that make Rsync transfers fast and reliable. Contact our 24/7 support team if you need help configuring your backup setup.


Related articles you might find useful:

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!