rsync synchronizes files and directories, locally or over SSH, transferring only the differences between source and destination. See robocopy for the Windows equivalent.


Basic Syntax

rsync <options> <source> <destination>

Trailing slash matters: <source-dir>/ copies the contents of the directory; <source-dir> (no slash) copies the directory itself into the destination.

Common Options

  • -a — Archive mode (recursive, preserves permissions, timestamps, symlinks, ownership).
  • -v — Verbose output.
  • -z — Compress data during transfer.
  • -h — Human-readable sizes.
  • -P — Show progress and allow resuming partial transfers.
  • --dry-run — Simulate the sync without making changes.
  • --delete — Delete files in the destination that no longer exist in the source.
  • --exclude=<pattern> — Skip files/directories matching the pattern.

Cheatsheet

Local Sync

# Copy contents of source-dir into dest-dir
rsync -avh <source-dir>/ <dest-dir>/

Remote Sync over SSH

# Push local files to a remote host
rsync -avz -e ssh <local-dir>/ <user>@<host>:<remote-dir>/
 
# Pull files from a remote host
rsync -avz -e ssh <user>@<host>:<remote-dir>/ <local-dir>/

Dry Run Before Deleting

# Preview what would be deleted without actually deleting
rsync -avh --delete --dry-run <source-dir>/ <dest-dir>/

Excluding Files

rsync -avh --exclude='*.log' --exclude='node_modules/' <source-dir>/ <dest-dir>/
# Reuses unchanged files from the previous backup via hard links
rsync -avh --link-dest=<previous-backup-dir> <source-dir>/ <new-backup-dir>/

Updating a Deployed App’s Source from a New Repo Checkout

# <new-repo> is a fresh checkout; <deployed-dir> holds source + config + data.
# Without --delete, rsync never removes destination-only paths, so data/
# and config.ini are left alone as long as <new-repo> doesn't contain
# paths with the same names. If it does (e.g. a template config.ini or
# fixture data/ dir checked into the repo), add --exclude=data
# --exclude=<config-dir> --exclude=config.ini --exclude=*.env to stop
# those from overwriting the deployed versions.
rsync -avh <new-repo>/ <deployed-dir>/