Backups with rsync
rsync copies directories fast and efficiently by transferring only the changed data — locally to another disk or to a remote server over SSH. This article covers the key flags -a, -v, -z, --delete, backing up a directory to another mHost server, and a wrapper script you can schedule with cron.
What you'll need
- An mHost VPS on Ubuntu 22.04/24.04, Debian 11/12, or AlmaLinux/Rocky Linux 8/9 — the source server you're backing up.
- A second server (for example, another mHost VPS) or a separate disk/partition on the same server — somewhere to keep the copies.
- SSH access as
rootor a user withsudorights: to the source server, and — for the remote case — to the destination server too. - The
rsyncpackage — usually already on the system, but worth checking (Step 1).
Key flags: -a, -v, -z, --delete
The -a (--archive) flag is the most important one: it's shorthand for -rlptgoD. rsync recurses into directories (-r), preserves symlinks as symlinks (-l), permissions (-p), modification times (-t), group (-g) and owner (-o), and device/special files (-D). Note that -a does not preserve ACLs (-A), extended attributes (-X), access times (-U), creation times (-N), or hard links (-H) — add those separately if you need them.
-v(--verbose) — prints the files being transferred and a short summary at the end. Use-vvfor more detail.-z(--compress) — compresses data on the fly before sending it over the network. Noticeably helpful on slow or bandwidth-limited links; on a fast link between servers in the same data center the gain is usually small, and close to zero for already-compressed files (archives, video, images).--delete— removes files on the receiving side that no longer exist on the sending side, so the copy stays an exact mirror instead of accumulating files for years.
Step 1. Make sure rsync is installed
rsync is needed on both sides — the source server and the server where the copies are kept.
Check whether it's already there:
rsync --versionIf the command isn't found, install the package.
Ubuntu / Debian:
sudo apt update
sudo apt install -y rsyncAlmaLinux / Rocky Linux:
sudo dnf install -y rsyncRepeat the check (and the install, if needed) on both servers.
Step 2. Set up SSH key access to the destination server
The wrapper script (Step 5) and cron (Step 6) will run rsync unattended — password login won't work here, you need SSH key authentication with no passphrase. Run the commands below on the source server, as the user the backup will run as (usually root).
- Generate a dedicated key for backups:
ssh-keygen -t ed25519 -f /root/.ssh/id_rsync_backup -N ""-N "" sets an empty passphrase — without it, a script launched from cron has no way to type the key's password.
- Copy the public key to the destination server —
backupin the example below can be any existing user on the destination server (includingroot) that has write access to the target directory:
ssh-copy-id -i /root/.ssh/id_rsync_backup.pub backup@203.0.113.10The first time, ssh-copy-id will ask for a password (or use your current key) to log in to backup@203.0.113.10 — that's expected; after that, the new key logs in without a password.
Replace backup@203.0.113.10 with the user and address of your own destination server.
- Confirm the connection works without a password:
ssh -i /root/.ssh/id_rsync_backup backup@203.0.113.10 "echo OK"If you get OK back with no password prompt, you're ready for rsync itself.
Step 3. Local directory sync
The local option is useful when you just need to mirror a directory to another disk, partition, or mounted network storage on the same server — no SSH, no second server.
Start with a dry run using -n (--dry-run), which changes nothing on disk:
rsync -avzn --delete /var/www/ /mnt/backup/www/Check the output, and if it looks right, run it for real (without -n):
rsync -avz --delete /var/www/ /mnt/backup/www/Step 4. Back up a directory to another server over SSH
The difference from the local case is that the destination is given as user@host:path, with a colon. Start with the same kind of dry run:
rsync -avzn --delete -e "ssh -i /root/.ssh/id_rsync_backup" /var/www/ backup@203.0.113.10:/backup/myserver/www/Check the output, then run it for real:
rsync -avz --delete -e "ssh -i /root/.ssh/id_rsync_backup" /var/www/ backup@203.0.113.10:/backup/myserver/www/What's new compared to the local case:
backup@203.0.113.10:/backup/myserver/www/— the user, the IP (or domain) of the destination server, and the path on it.-e "ssh -i /root/.ssh/id_rsync_backup"— explicitly tells rsync to use SSH with the specific key file from Step 2. If the key were saved under a standard name (~/.ssh/id_ed25519orid_rsa) on the standard port 22, you could skip-eentirely — modern rsync already uses SSH by default.-eis needed when SSH needs non-default settings: a specific key file, or a non-standard port (-e "ssh -p 2222 -i /root/.ssh/id_rsync_backup").
As with the local case, create the destination directory in advance:
ssh -i /root/.ssh/id_rsync_backup backup@203.0.113.10 "mkdir -p /backup/myserver/www"Step 5. A wrapper script for regular backups
Instead of typing the command by hand, wrap it in a script: with logging, error handling, and protection against overlapping runs if the previous backup hasn't finished yet.
Create the file:
sudo nano /usr/local/sbin/rsync-backup.shContents:
#!/usr/bin/env bash
set -euo pipefail
# Source directory and backup destination
SRC="/var/www/"
DEST_USER="backup"
DEST_HOST="203.0.113.10"
DEST_PATH="/backup/myserver/www/"
SSH_KEY="/root/.ssh/id_rsync_backup"
LOG_FILE="/var/log/rsync-backup.log"
LOCK_FILE="/var/lock/rsync-backup.lock"
# Prevent overlapping runs if the previous backup is still going
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
echo "$(date '+%Y-%m-%d %H:%M:%S') Previous backup is still running, exiting" >> "$LOG_FILE"
exit 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') Backup started" >> "$LOG_FILE"
if rsync -avz --delete -e "ssh -i ${SSH_KEY}" "$SRC" "${DEST_USER}@${DEST_HOST}:${DEST_PATH}" >> "$LOG_FILE" 2>&1; then
echo "$(date '+%Y-%m-%d %H:%M:%S') Backup finished OK" >> "$LOG_FILE"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') Backup FAILED" >> "$LOG_FILE"
exit 1
fiMake the script executable and root-only:
sudo chmod 700 /usr/local/sbin/rsync-backup.shRun it by hand and check the log:
sudo /usr/local/sbin/rsync-backup.sh
tail -n 20 /var/log/rsync-backup.logStep 6. Schedule the script with cron
Open root's crontab (the same user the SSH key was set up for in Step 2):
sudo crontab -eAdd a line — for example, a daily run at 02:30:
30 2 * * * /usr/local/sbin/rsync-backup.shThe script logs to /var/log/rsync-backup.log on its own, so redirecting output in crontab too isn't necessary.
For a detailed look at cron syntax, other scheduling options (hourly, specific weekdays), and common mistakes, see Automating backups with cron.
What's next
rsync with --delete only keeps the latest state of a directory, not versions or history: if a file quietly got corrupted a while back, there's no older copy left — the same --delete removed it there too. For versioned, encrypted, deduplicated backups, see the article on restic: Backups with restic.