rsync and cron Backups on a VPS: A Reliable Pipeline That Costs Nothing Extra

Backup software is a crowded market: Borg, restic, rclone, and a dozen hosted services all promise bulletproof recovery. For a single VPS, though, the most underrated setup is also the oldest: rsync plus cron. Both ship with virtually every Linux distribution, the pipeline is easy to audit line by line, and there is no subscription fee. What separates a real backup system from a folder of copies is design discipline — this guide covers the pipeline, the retention scheme, and the restore drill that makes it trustworthy.

The three-stage pipeline

Every serious backup has three stages: capture the data in a consistent state, stage it locally, then push it offsite. Stage one dumps databases and snapshots files into a staging directory. Stage two holds that staging copy long enough for the transfer to complete. Stage three pushes it to a host that is not your VPS — ideally a different provider or region, so a datacenter outage does not take your backups with it. If you have not decided where your production box lives yet, our VPS comparison table is a good place to start comparing regions and plans.

Dump databases without locking your app

Files can be copied while the server runs, but databases need a consistent snapshot. Use the non-locking dump flags so your application never stalls:

# MySQL / MariaDB: consistent snapshot without blocking writes
mysqldump --single-transaction --routines --triggers myapp > /var/backups/staging/myapp.sql

# PostgreSQL: custom format compresses well and restores selectively
pg_dump -Fc myapp > /var/backups/staging/myapp.dump

# SQLite: use the online backup API, not a raw file copy
sqlite3 /var/lib/myapp/app.db ".backup '/var/backups/staging/app.db'"

Write dumps into the same staging directory that rsync later mirrors, so one transfer covers both files and databases.

Push offsite over SSH

The transfer itself is a single rsync invocation. Create a dedicated user on the backup host with no shell and an SSH key restricted to rsync; then run:

rsync -az --delete --partial \
  -e "ssh -i /root/.ssh/backup_key -p 2222" \
  /var/backups/staging/ \
  [email protected]:/backups/vps1/current/

-a preserves permissions and timestamps, --partial resumes an interrupted transfer instead of restarting it, and --delete mirrors deletions so the offsite copy matches the staging directory exactly. Combined with the hardlink rotation below, --delete is what keeps snapshots honest without duplicating storage.

Retention with hardlink rotation

Rotating snapshots normally means copying data N times. With --link-dest, unchanged files become hardlinks to the previous snapshot, so 14 snapshots cost barely more than one full backup plus the bytes that actually changed. The rotation script shifts directories before each run:

rm -rf /backups/vps1/backup.3
mv /backups/vps1/backup.2 /backups/vps1/backup.3
mv /backups/vps1/backup.1 /backups/vps1/backup.2
mv /backups/vps1/backup.0 /backups/vps1/backup.1

rsync -a --delete --link-dest=../backup.1 \
  /var/backups/staging/ /backups/vps1/backup.0/
Retention tierCopies keptExtra disk cost
Daily7~size of one backup (hardlinked)
Weekly4~same, hardlinked
Monthly3~same, hardlinked

Because unchanged files are hardlinks, a month of daily snapshots typically occupies only slightly more space than a single full backup plus the changed bytes.

Schedule it like a human

cron is the obvious choice, but pick an odd minute so your backup does not collide with everyone else’s 03:00 stampede, and log the exit status:

17 3 * * * /usr/local/sbin/vps-backup.sh >> /var/log/vps-backup.log 2>&1

If you prefer systemd, a timer with OnCalendar=*-*-* 03:17:00 and Persistent=true catches up after a reboot — something cron silently skips. Whichever you use, wire the script’s exit code to a notification (cron mail, or a healthchecks.io ping). A backup that fails silently is not a backup at all.

The restore drill

Backups you have never restored are guesses. Once a month, on a scratch VPS or container: restore the database dump and verify row counts, unpack the file snapshot and compare checksums with diff -r, and time the whole procedure. Record the elapsed time in the backup log — if a restore starts taking hours, you want to discover that now, not during an incident. It also keeps the drill honest: run your VPS workload against the restored copy for a few minutes to catch missing directories or wrong permissions.

Encrypt what leaves your server

If the backup host is a machine you control, SSH transport encryption may be enough. If backups land on object storage or a third-party host, encrypt the dumps with age or GPG before staging them. One trade-off: encryption defeats rsync’s hardlink dedup for those files, so encrypt only the database dumps and keep plain files for the file tree, or use a tool like rclone crypt for object storage.

The whole pipeline runs on software you already have, which means the only real cost is discipline. If you are setting up a second VPS as the offsite target, compare providers on our VPS comparison table — a cheap box in a different region is all this design needs.

Looking for a host for both the production server and the backup target? InterServer’s VPS plans are priced low enough that a dedicated backup VPS in a second location is a realistic addition to any setup.

Leave a Reply