I needed to sync a local directory to a remote server, excluding some files.
TLDR: Common rsync patterns Link to heading
# Basic sync (local to remote)
rsync -avz ./ user@server:/path/to/dest/
# Sync excluding common directories
rsync -avz --exclude .venv --exclude node_modules --exclude .git ./ user@server:/dest/
# Mirror (sync and delete removed files)
rsync -avz --delete ./ user@server:/dest/
# Dry run (preview changes)
rsync -avzn ./ user@server:/dest/
# Pull from remote to local
rsync -avz user@server:/path/to/source/ ./local/
# Copy between two remote servers (via local machine)
rsync -avz user1@server1:/source/ user2@server2:/dest/
Basic sync Link to heading
rsync -avz ./ user@server:/path/to/dest/
The flags:
-aarchive mode (preserves permissions, timestamps, etc.)-vverbose-zcompress during transfer
Exclude files Link to heading
Single exclusion:
rsync -avz --exclude .venv ./ user@server:/dest/
Multiple exclusions:
rsync -avz --exclude .venv --exclude node_modules --exclude .git ./ user@server:/dest/
Exclude from file Link to heading
Create .rsyncignore:
.venv
node_modules
.git
*.pyc
__pycache__
Then:
rsync -avz --exclude-from=.rsyncignore ./ user@server:/dest/
Dry run workflow Link to heading
I always do a dry run first before syncing anything important. Rsync is powerful and you can easily delete files by mistake if you get the command wrong.
rsync -avzn ./ user@server:/dest/
The -n flag means dry run. This shows you exactly what would be transferred without actually doing it.
Look for these things in the output:
- Files being deleted (if using
--delete) - Unexpected directories being synced
- Large files you forgot to exclude
Once you’re happy with the dry run output, remove the -n flag and run it for real. This habit has saved me multiple times from syncing gigabytes of cache files or accidentally wiping a directory.
Delete files on destination Link to heading
Remove files from destination that don’t exist locally:
rsync -avz --delete ./ user@server:/dest/
Be careful with this one - it will delete files on the destination that don’t exist in your source. Always dry run first.
rsync over SSH tips Link to heading
Rsync uses SSH by default for remote transfers. A few things that make this nicer:
Use SSH config to avoid typing full hostnames:
# ~/.ssh/config
Host prod
HostName server.example.com
User deploy
IdentityFile ~/.ssh/deploy_key
Then you can just do:
rsync -avz ./ prod:/path/to/dest/
Specify a different SSH port:
rsync -avz -e "ssh -p 2222" ./ user@server:/dest/
Show progress for large transfers:
rsync -avz --progress ./ user@server:/dest/
The --progress flag shows a progress bar for each file. Useful when syncing large codebases or data directories.
Bandwidth limiting (useful on slow connections):
rsync -avz --bwlimit=1000 ./ user@server:/dest/
This limits the transfer to 1000 KB/s so you don’t saturate your connection.
Why rsync instead of scp? Link to heading
I used to use scp for copying files to servers, but rsync is much better:
- Only transfers files that have changed (much faster for repeat syncs)
- Can resume interrupted transfers
- Better handling of symlinks and permissions
- More control over what gets synced
The only time I still use scp is for quick one-off file copies where I don’t care about efficiency.
Links Link to heading
- rsync examples - official examples from the rsync team
- rsync man page - comprehensive reference for all options