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:

  • -a archive mode (preserves permissions, timestamps, etc.)
  • -v verbose
  • -z compress 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.