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=progress2 shows 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"}
  • -H hard links, -A ACLs, -X extended attributes, -x stay on one filesystem.
  • --numeric-ids keeps 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

  1. Sync while the site is live: rsync -a --delete /var/www/ new:/var/www/ (takes a while).
  2. 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.
  3. 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.

--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 found on 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.

  • Linux
  • rsync
  • Backups