It’s 10 PM on a Friday night. The primary web server at a major healthcare organization crashes due to a hardware failure. The filesystem is corrupted. The on-call DBA asks for the last valid backup. The sysadmin checks, only to discover the last full backup is three days old. The intermediate transaction logs are gone. To prevent this nightmare scenario, I standardized an automated Linux rsync backup system across every server I manage. Rsync transfers only the modified data blocks, cutting network traffic by 90% compared to scp and shrinking the backup window from hours to minutes. In this guide, I’ll show you how to configure synchronization, write a robust script with logging and notifications, and automate everything with cron.
Rsync in 60 Seconds: What It Is and When to Use It vs scp/tar
Rsync is a file synchronization tool that uses a checksum algorithm to determine the differences between source and destination. If you have a 1 GB log file and append 10 MB of new lines, rsync only copies those 10 MB. Scp, on the other hand, copies the entire file from scratch. Tar creates monolithic archives that consume space and require time to extract a single file during an emergency restore.
Use rsync when you need to keep directories synchronized, create incremental backups, or migrate data between servers. Use scp only for one-off transfers of small files. Use tar for archiving old data long-term to move to tape or object storage.
Basic Syntax and Essential Options essential (-a, -v, -z, –delete, –exclude)
Rsync syntax follows the pattern rsync [options] source destination. The trailing slash in paths is critical: /source/ copies the contents of the folder, while /source copies the folder itself.
The options I use 99% of the time are:
-a(archive): Preserves permissions, owners, groups, symlinks, and timestamps. It’s the foundation of any reliable backup.-v(verbose): Shows the transferred files.-z(compress): Compresses data during transfer. It reduces bandwidth consumption on slow links.--delete: Deletes files in the destination that no longer exist in the source. Creates an exact mirror.--exclude: Skips files or directories matching the pattern.
Here is how to exclude log files and temporary directories:
rsync -avz --exclude='*.log' --exclude='tmp/' /source/ /dest/
Before running any command in production, use a dry run with the -n option. It simulates the transfer without writing anything:
rsync -avzn /source/ /dest/ # dry run
Local Backup: Synchronizing Directories
Local backup is the starting point for testing syntax. Suppose you want to sync data from a primary disk to a backup disk mounted on the same server:
rsync -avz /source/ /destination/
If you want the destination to be an exact copy of the source, including the deletion of removed files, add --delete:
rsync -avz --delete /source/ /destination/
Be careful with --delete. If you accidentally reverse the source and destination, you will wipe out your original data. Always test with -n first.
Remote Backup via SSH: Configuration and Authentication
Synchronizing directories between remote servers is the most common use case. Rsync uses SSH as its default transport, ensuring data encryption in transit.
rsync -avz -e ssh /var/www/ utente@backup-server:/backup/www/
The -e ssh option is redundant in recent versions but useful for specifying non-standard ports or custom SSH keys: -e 'ssh -p 2222 -i /home/user/.ssh/id_rsa_backup'.
To automate the process with cron, you must configure public key authentication between the servers. Without this setup, the cron job will hang waiting for a password. [Read also: SSH Without Password Linux: Complete Guide to Public Key Authentication]
Full Backup Script with Logging and Email Notification
A nightly backup must leave a trace of its outcome. I wrote a script that performs the sync, logs the output to a file, and sends an email in case of an error.
#!/bin/bash
SOURCE_DIR="/var/www/"
DEST_USER="utente"
DEST_HOST="backup-server"
DEST_DIR="/backup/www/"
LOG_FILE="/var/log/rsync_backup_$(date +%Y%m%d).log"
EMAIL_ADMIN="[email protected]"
rsync -avz --delete -e ssh $SOURCE_DIR ${DEST_USER}@${DEST_HOST}:${DEST_DIR} > $LOG_FILE 2>&1
if [ $? -ne 0 ]; then
mail -s "ERRORE BACKUP RSYNC su $(hostname)" $EMAIL_ADMIN < $LOG_FILE
fi
Save the script in /opt/scripts/backup_rsync.sh, make it executable with chmod +x, and test it manually before adding it to cron.
Automating with Cron: Practical Examples
Cron executes commands at fixed intervals. To run the backup every night at 02:00, edit the root user’s crontab with crontab -e and add:
0 2 * * * /opt/scripts/backup_rsync.sh
If you prefer more frequent backups of critical data, every 4 hours:
0 */4 * * * /opt/scripts/backup_rsync.sh
Make sure the PATH variable in the script or crontab includes the paths to the rsync and ssh binaries. Cron has a much more restricted environment than your interactive shell.
Incremental vs Full Rsync: When to Use What
Rsync is inherently incremental at the block level. It transfers only the modified parts of files. However, you often need to maintain historical snapshots (folder-dated incremental backups) without wasting space.
The --link-dest option solves this problem. It creates a new directory for the current backup and creates hard links for files identical to the previous backup. Modified files are actually copied.
YESTERDAY=$(date -d "yesterday" +"%Y-%m-%d")
rsync -avz --delete --link-dest=/backup/www/$YESTERDAY /var/www/ utente@backup-server:/backup/www/$(date +"%Y-%m-%d")/
This strategy offers the best of both worlds: fast restoration from dated folders and disk consumption limited only to changed data.
Handling Errors: Exit Codes and Telegram Notifications
A silent backup script is a risk. If it fails and no one notices, you don’t have a backup. Rsync returns specific exit codes that you can intercept. Code 0 indicates success, 24 indicates a partial transfer (files vanished during sync, typical of rotating logs), and 23 indicates a permission error.
For modern infrastructures, email notifications often get lost in the noise. Telegram notifications are more immediate. Create a bot on Telegram via BotFather, get the token and chat_id, then add this function to your script:
send_telegram() {
MESSAGE=$1
TOKEN="IL_TUO_TOKEN_BOT"
CHAT_ID="IL_TUO_CHAT_ID"
curl -s -X POST "https://api.telegram.org/bot${TOKEN}/sendMessage" -d chat_id=${CHAT_ID} -d text="${MESSAGE}" > /dev/null
}
if [ $? -ne 0 ] && [ $? -ne 24 ]; then
send_telegram "ERRORE CRITICO BACKUP RSYNC su $(hostname). Exit code: $?"
fi
Ignoring exit code 24 is an operational choice. Rotating log files often vanish during transfer. If your backup includes active log directories, code 24 is perfectly normal.
Conclusion
A backup doesn’t exist until you’ve tested the restore. Rsync gives you the tools to synchronize data efficiently, reduce bandwidth, and create incremental snapshots. Configure passwordless SSH authentication, build a script with logging and Telegram notifications, and automate it with cron. Check the logs regularly and test restoring a single file at least once a month. When the primary disk fails, the difference between a 10-minute outage and a 3-day catastrophe will depend entirely on how you configured your automated Linux rsync backup.