Rsync emerged in 1996 as an answer to a simple question: how do you transfer file changes over a network without shipping full copies every time? Andrew Tridgell wrote an algorithm that compares data blocks using checksums and transfers only the differences. Three decades later, rsync remains the tool sysadmins reach for in their first days on Linux and never put down.
The principle is straightforward: rsync analyzes files at source and destination, calculates the difference between them, transfers only changed blocks. The first sync of a 100 GB directory takes as long as the data transfer requires. The next run is several times faster because only what changed since last time gets copied. That's what makes rsync indispensable for backups and server-to-server synchronization.
Installing rsync
Most distributions ship with rsync pre-installed. If not, one command handles it.
On Ubuntu/Debian:
apt update && apt install rsync -y
On CentOS/AlmaLinux/Rocky Linux:
dnf install rsync -y
Check the version:
rsync --version
Typical output: rsync version 3.2.7. The 3.x branch carries all modern features including xxhash support for faster file comparison.
Command Structure
General rsync syntax:
rsync [options] source destination
Source and destination can be:
- Local paths:
/home/user/data/ - Remote via SSH:
user@server:/path/to/dir/ - Rsync daemon:
rsync://server/module/
One important detail about the trailing slash: it changes rsync's behavior. /home/user/data (no slash) copies the data directory itself into the destination. /home/user/data/ (with slash) copies the directory's contents. This distinction is worth internalizing early — it causes confusion regularly.
Core Flags
Most tasks are handled by the same set of flags sysadmins type on autopilot:
rsync -avz source destination
What each letter means:
-a (archive) — a combo flag that enables several options at once: recursive copying (-r), preserving symbolic links (-l), preserving permissions (-p), preserving timestamps (-t), preserving owner (-o) and group (-g). The -a flag is the standard pick for most tasks because it reproduces files with maximum fidelity.
-v (verbose) — detailed output. Rsync prints every file it copies. Useful during debugging, can be dropped from scripts for clean output.
-z (compress) — compresses data during transfer. Reduces network load at the cost of CPU. Worth using for text files (configs, logs, code), pointless for already-compressed archives or media files.
Other frequently used flags:
--progress — shows transfer progress for each file. Convenient for large files to confirm the process is moving.
--delete — removes files at the destination that no longer exist at the source. Turns rsync from a simple copier into a mirror synchronizer.
--exclude — excludes files or directories from sync by pattern.
--dry-run (-n) — runs rsync without actually copying anything, just shows what would happen. Invaluable for checking a command before the real run.
--checksum (-c) — compares files by checksum rather than size and modification time. Slower but more reliable for critical data.
--bwlimit — limits bandwidth in KB/s. Useful to keep synchronization from consuming the entire network link.
Local Synchronization
The simplest case — copying data between directories on the same server.
Sync a website into a backup directory:
rsync -av /var/www/html/ /backup/www/
The first run copies everything. Second and subsequent runs — only changes.
Add --delete so the backup exactly mirrors the source (files deleted from source get removed from backup too):
rsync -av --delete /var/www/html/ /backup/www/
Exclude cache and temporary files:
rsync -av --delete \
--exclude='cache/' \
--exclude='tmp/' \
--exclude='*.log' \
/var/www/html/ /backup/www/
Check what would happen without actually copying anything:
rsync -av --delete --dry-run /var/www/html/ /backup/www/
Rsync outputs a list of files that would be copied or deleted. Once satisfied the command is correct, run it without --dry-run.
Remote Synchronization Over SSH
Rsync runs over SSH — no additional daemons needed. The only requirements are SSH running on the target server and rsync installed there.
Push local data to a remote server:
rsync -avz /var/www/html/ user@192.168.1.100:/backup/www/
Pull data from a remote server to local:
rsync -avz user@192.168.1.100:/var/www/html/ /local/backup/
Sync between two remote servers (through the local machine as intermediary):
rsync -avz user@server1:/data/ user@server2:/data/
Working over SSH, rsync prompts for a password on each run — inconvenient for automation. The fix is SSH key authentication:
# Generate a key if you don't have one
ssh-keygen -t ed25519 -C "rsync backup key"
# Copy the public key to the remote server
ssh-copy-id user@192.168.1.100
After this rsync runs without password prompts — safe to put in cron.
Non-standard SSH Port
rsync -avz -e "ssh -p 2222" /var/www/html/ user@192.168.1.100:/backup/www/
The -e flag tells rsync which command to use for the connection. Any SSH parameters work: port, specific key, timeouts.
Specific Key for the Connection
rsync -avz -e "ssh -i /root/.ssh/backup_key" /data/ user@backup-server:/data/
Incremental Backups with Version History
Simple synchronization keeps a current copy but doesn't protect against accidental deletion or file corruption — the change immediately replicates into the backup. For version history, the hard link technique is used.
The idea: each backup looks like a full copy, but unchanged files aren't duplicated — they're represented as hard links pointing to the file from the previous backup. This saves space while giving access to any historical snapshot.
The flag --link-dest tells rsync to use hard links to files from this directory for files that haven't changed.
A daily backup script keeping 30 days of history:
#!/bin/bash
SOURCE="/var/www/html/"
DEST="/backup/www"
DATE=$(date +%Y-%m-%d)
LATEST="$DEST/latest"
# Create today's backup directory
mkdir -p "$DEST/$DATE"
# Sync using yesterday's backup as the baseline
rsync -av --delete \
--link-dest="$LATEST" \
"$SOURCE" "$DEST/$DATE/"
# Update the latest symlink
ln -sfn "$DEST/$DATE" "$LATEST"
# Remove backups older than 30 days
find "$DEST" -maxdepth 1 -type d -name "????-??-??" \
-mtime +30 -exec rm -rf {} \;
echo "Backup completed: $DEST/$DATE"
Result after a week of running:
/backup/www/
├── 2026-04-22/ (full copy)
├── 2026-04-23/ (new files only + hardlinks)
├── 2026-04-24/ (new files only + hardlinks)
├── 2026-04-25/ (new files only + hardlinks)
├── 2026-04-26/ (new files only + hardlinks)
├── 2026-04-27/ (new files only + hardlinks)
├── 2026-04-28/ (new files only + hardlinks)
└── latest -> 2026-04-28 (symlink to latest)
Each directory looks like a complete copy — you can enter any of them and find a file exactly as it was that day. On disk they collectively take up little more than a single copy — additional space is consumed only by new and changed files.
Automation via Cron
Manual rsync runs are useful for one-off tasks, but the tool's real value emerges in automatic mode.
Open crontab:
crontab -e
Nightly backup at 3:00 AM:
0 3 * * * rsync -az --delete /var/www/html/ /backup/www/ >> /var/log/rsync-backup.log 2>&1
Sync to a remote server every hour:
0 * * * * rsync -az -e "ssh -i /root/.ssh/backup_key" /data/ backup@192.168.1.100:/data/ >> /var/log/rsync.log 2>&1
Database backup (dump + rsync) every 6 hours:
0 */6 * * * /usr/local/bin/backup-db.sh
Where backup-db.sh:
#!/bin/bash
DATE=$(date +%Y%m%d-%H%M)
mysqldump -u root -p'password' --all-databases > /tmp/db-$DATE.sql
rsync -az /tmp/db-$DATE.sql backup@192.168.1.100:/backup/db/
rm /tmp/db-$DATE.sql
For production scripts, add result notifications. A simple email version:
0 3 * * * rsync -az --delete /var/www/ /backup/www/ && echo "Backup OK" | mail -s "Backup success" admin@your-domain.com || echo "Backup FAILED" | mail -s "Backup ERROR" admin@your-domain.com
Monitoring and Logging
Rsync doesn't maintain a persistent log by itself — each run outputs results to stdout. To retain history, redirect output to a file.
Basic logging via redirection:
rsync -av /data/ /backup/ >> /var/log/rsync.log 2>&1
For more structured logs use --log-file:
rsync -av --log-file=/var/log/rsync.log /data/ /backup/
With timestamps:
rsync -av --log-file=/var/log/rsync.log \
--log-file-format="%t %f %b" \
/data/ /backup/
The %t %f %b format outputs: timestamp, filename, bytes transferred.
To keep logs from growing without bound, configure logrotate. Create /etc/logrotate.d/rsync:
/var/log/rsync.log {
daily
rotate 30
compress
missingok
notifempty
}
Logrotate archives the log daily, keeps 30 versions and compresses older ones. The file grows predictably.
Check the last lines of the log:
tail -50 /var/log/rsync.log
Count how many files were transferred in the last run:
grep "Number of files transferred" /var/log/rsync.log | tail -1
Rsync Daemon for Regular Synchronization
For situations where many clients connect to a server, or fine-grained module permissions are needed — an rsync daemon can be configured. It listens on port 873 and doesn't require SSH.
Create config /etc/rsyncd.conf:
# Global settings
uid = rsync
gid = rsync
use chroot = yes
max connections = 10
timeout = 300
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid
# Backup module
[backup]
path = /backup/data
comment = Backup storage
read only = no
list = yes
auth users = backupuser
secrets file = /etc/rsyncd.secrets
hosts allow = 192.168.1.0/24
hosts deny = *
Password file /etc/rsyncd.secrets:
backupuser:strong-password
Permissions on the secrets file must be 600, otherwise rsync refuses to start:
chmod 600 /etc/rsyncd.secrets
Start the daemon:
systemctl enable rsync
systemctl start rsync
Client connection to the daemon:
rsync -av backupuser@192.168.1.100::backup /local/backup/
Double colon :: indicates connecting to an rsync daemon, single : goes through SSH.
Practical Use Cases
Deployment Synchronization
When updating a site, rsync minimizes downtime — only changed files get replaced:
rsync -avz --delete \
--exclude='.git' \
--exclude='node_modules' \
--exclude='.env' \
./build/ user@production-server:/var/www/html/
System Config Backup
Regularly snapshot system configurations:
rsync -avz \
/etc/ \
/home/ \
/root/ \
backup@backup-server:/system-backup/$(hostname)/
Multi-Server Mirror
Keep a shared uploads directory in sync across cluster nodes:
for server in web1 web2 web3; do
rsync -az /var/www/uploads/ $server:/var/www/uploads/
done
Partial Recovery After Failure
Rsync resumes interrupted transfers from where they left off — especially valuable for large files. The --partial flag keeps partially transferred files:
rsync -avz --partial --progress \
user@backup-server:/backup/large-archive.tar.gz \
/restore/
Common Errors and Solutions
Permission Denied with Root-Owned Directories
Rsync running as a regular user tries to copy files owned by root. Solution — run as root or use --rsync-path to invoke rsync with elevated privileges on the remote side:
rsync -avz --rsync-path="sudo rsync" user@server:/etc/ /backup/etc/
Slow Synchronization on Large Directories
Rsync builds a full file list before starting transfers. For directories with millions of files this takes minutes. Speed it up with --no-inc-recursive to build the list incrementally:
rsync -avz --no-inc-recursive /huge-directory/ /backup/
Files with Spaces and Special Characters in Names
Rsync handles them correctly, but if passing a file list via --files-from, ensure the file contains one path per line without extra escaping.
Rsync Timing Out Over Slow Connections
Add --timeout to set a connection timeout in seconds:
rsync -avz --timeout=300 user@slow-server:/data/ /backup/
Different rsync Versions on Source and Destination
Older versions may not support certain flags. When working with legacy systems, check the version and remove incompatible options. The minimal flag set (-av) works everywhere.
Performance and Optimization
Rsync uses MD5 for checksums by default. Modern versions support faster algorithms:
rsync -avz --checksum-choice=xxh128 /data/ /backup/
xxHash is several times faster than MD5, noticeably so on large data volumes. Available in rsync 3.2.0+.
If transfer speed matters more than security (working on a private network), specify a fast SSH cipher:
rsync -avz -e "ssh -c aes128-gcm@openssh.com" /data/ user@server:/backup/
AES128-GCM outperforms ChaCha20 on modern processors with hardware AES acceleration.
Parallel sync of multiple directories using GNU parallel:
parallel rsync -az {} backup-server:/backup/{/} ::: /var/www /var/mail /home
Each directory syncs in a separate process simultaneously.
Rsync vs Alternatives
Rsync vs scp
SCP is a simple utility for one-off file copying. Rsync is for regular synchronization. SCP always transfers a file in full, rsync transfers only changes. For moving a small file once, the difference is negligible. For regular backups of large directories, rsync wins by an order of magnitude.
Rsync vs rclone
Rclone is rsync's equivalent for cloud storage (S3, Google Drive, Backblaze, Dropbox). If the destination is a local server or another Linux machine, rsync is faster and more reliable. If the target is cloud storage, rclone is the right tool.
Rsync vs Restic
Restic is a modern backup tool with deduplication, encryption, and cloud storage support. More complex to configure but more capable. Rsync is simpler, faster for local tasks, and doesn't encrypt data by default.
Rsync vs Lsyncd
Lsyncd is a wrapper around rsync that watches filesystem changes via inotify and triggers synchronization in real time. Suitable when near-zero-latency sync is needed — for example, mirroring across multiple application servers.
Frequently Asked Questions
What is rsync and what is it used for?
Rsync is a command-line utility for file synchronization and backup on Linux. The key difference from simple copying: rsync transfers only changed parts of files, not entire files. This makes repeated runs significantly faster. Used for server backups, data synchronization between machines, and deploying files to production.
How do I run rsync without a password prompt?
Set up SSH keys: generate a key pair with ssh-keygen -t ed25519, copy the public key to the remote server via ssh-copy-id user@server. After that rsync connects automatically without a password — convenient for cron jobs and scripts.
What is the difference between rsync with and without --delete?
Without --delete, rsync only adds and updates files at the destination — files deleted from the source stay there. With --delete, the destination becomes an exact mirror of the source: files absent from the source get deleted from the destination too. For backups, --delete is the right choice, but always verify first with --dry-run.
How do I create incremental backups with version history using rsync?
Use the --link-dest flag — it creates hard links to unchanged files from the previous backup instead of copying them. Each backup looks like a full copy but only occupies disk space for changed files. Typical approach: daily snapshots retained for 30 days, automatic cleanup via find -mtime +30.
How do I limit rsync speed to avoid saturating the network link?
Use the --bwlimit=NUMBER flag where the number is the limit in KB/s. For example, rsync -avz --bwlimit=10240 /data/ user@server:/backup/ caps speed at 10 MB/s. Useful when syncing over slow links or when rsync runs alongside production traffic.
Does rsync work on Windows?
Not natively. On the Linux side rsync works as normal; on Windows you need Cygwin, WSL, or Windows ports like DeltaCopy or cwRsync. For regular synchronization between Windows and Linux, rclone or WinSCP with SCP/SFTP support are often simpler options.
Conclusion
Rsync handles the majority of file transfer and synchronization tasks on Linux. Three decades of active use with no real competition for its specific class of problems speaks for itself.
For starters, two templates are enough to memorize:
Local sync:
rsync -avz --delete /source/ /destination/
Remote via SSH:
rsync -avz --delete /source/ user@server:/destination/
From there, add flags as needs arise: exclusions, bandwidth limits, checksum verification, incremental snapshots. Rsync scales from copying a single file to a corporate backup system with version history — all with one tool.
A VPS on THE.Hosting with NVMe drives in RAID-10 makes a solid backup server foundation. High I/O speed accelerates both writing incremental snapshots and restoring data when it matters most. Support is available 24/7 via Telegram for any infrastructure configuration questions.