rsync copies files efficiently: it transfers only what changed, preserves permissions and timestamps, works over SSH, and can resume interrupted transfers. It’s the tool for server migrations, mirrored directories and simple backups.
rsync [options] SOURCE DESTINATION
The options you’ll use most
rsync -avh --progress source/ destination/
-a(archive) — recursive, and preserves permissions, timestamps, symlinks, owner and group (owner/group only when run as root).-v— verbose;-h— human-readable sizes.--progress— per-file progress;--info=progress2shows one overall progress line instead.-z— compress during transfer. Helps on slow links with compressible data; skip it on fast networks or for already-compressed files.
The trailing slash rule
This is the one thing everyone gets wrong once:
rsync -a /var/www/site /backup/ # creates /backup/site/...
rsync -a /var/www/site/ /backup/ # copies the CONTENTS into /backup/...
A trailing slash on the source means “the contents of this directory”. Without it, the directory itself is copied. The destination’s trailing slash doesn’t matter.
Copy to and from a remote server
rsync uses SSH automatically when a path contains host::
rsync -avz /var/www/site/ [email protected]:/var/www/site/ # push
rsync -avz [email protected]:/var/www/site/ /var/www/site/ # pull
rsync must be installed on both machines. Custom SSH port or key:
rsync -av -e "ssh -p 2222 -i ~/.ssh/deploy_key" src/ user@host:/dest/
Always dry-run first
rsync -avn --delete src/ dest/
-n (--dry-run) shows what would happen without changing anything. Make it a habit before any command with --delete.
Mirror with –delete
By default rsync never deletes files at the destination. To make the destination an exact mirror:
rsync -av --delete /var/www/site/ backup-host:/mirror/site/
Files deleted from the source are deleted from the destination. Swap source and destination by accident and you’ll wipe your source — hence the dry run.
Safer variations:
rsync -av --delete --backup --backup-dir=/mirror/deleted-$(date +%F) src/ dest/
rsync -av --delete --max-delete=100 src/ dest/ # abort if more than 100 deletions
Exclude and include
rsync -av --exclude='node_modules' --exclude='*.log' --exclude='.git/' src/ dest/
rsync -av --exclude-from='exclude.txt' src/ dest/
rsync -av --include='*/' --include='*.jpg' --exclude='*' src/ dest/ # only .jpg files, keep structure
Patterns are matched relative to the source root; a leading / anchors to it (--exclude='/cache/' matches only the top-level cache directory).
Resume large transfers
rsync -avP bigfile.iso user@host:/data/
-P = --partial --progress: interrupted files are kept and resumed on the next run. For very large files over flaky links, add --append-verify.
Limit bandwidth
Don’t saturate a production server’s uplink during business hours:
rsync -av --bwlimit=20M src/ user@host:/dest/ # ~20 MB/s
Preserve everything (server migrations)
sudo rsync -aHAXx --numeric-ids --info=progress2 / root@new-server:/mnt/newroot/ \
--exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"}
-Hhard links,-AACLs,-Xextended attributes,-xstay on one filesystem.--numeric-idskeeps UIDs/GIDs as numbers instead of mapping names.
For website migrations, copying /var/www, the database dump and the web server config is usually cleaner than cloning the whole OS — see the move a website to a VPS.
Two-step migration with minimal downtime
- Sync while the site is live:
rsync -a --delete /var/www/ new:/var/www/(takes a while). - Put the site in maintenance mode, dump the database, run the same rsync again — it only sends what changed in the meantime, so it’s fast.
- Import the database on the new server and switch DNS.
Compare without copying
rsync -avnc src/ dest/ # -c compares checksums instead of size+time
Lists files whose contents differ — useful to verify a migration.
Incremental snapshots with hard links
--link-dest creates dated snapshots where unchanged files are hard links to the previous snapshot, using almost no extra space:
TODAY=$(date +%F)
rsync -a --delete --link-dest=/backups/latest /var/www/ /backups/$TODAY/
ln -sfn /backups/$TODAY /backups/latest
Each directory looks like a full copy, but only changed files consume space. Tools like rsnapshot automate this pattern.
Run rsync from cron
Use a dedicated SSH key without a passphrase, restricted on the receiving side, and log output:
15 3 * * * rsync -a --delete -e "ssh -i /root/.ssh/backup_key" /var/www/ backup@backup-host:/srv/backups/web1/ >> /var/log/rsync-backup.log 2>&1
See the crontab guide for scheduling details.
rsync vs scp vs tar
- scp — quick one-off copies; re-sends everything each time.
- rsync — repeated syncs, large trees, resumable transfers.
- tar over SSH — fastest for a one-time copy of millions of small files.
Troubleshooting
rsync: command not foundon the remote side — install rsync there.Permission denied— the remote user can’t write to the destination, or can’t read root-owned files at the source. Use--rsync-path="sudo rsync"with suitable sudo rules, or run as root.- Everything is re-copied each time — timestamps aren’t preserved (missing
-a), or the destination filesystem (e.g. FAT, some network mounts) can’t store them; try--modify-window=1.
Backing up many servers? A Storage VPS makes a simple rsync target with plenty of disk space.