rsync Over SSH: Efficient File Synchronisation

rsync is the workhorse of Linux file transfer. It uses a delta-transfer algorithm that sends only the differences between source and destination files, making it dramatically faster than scp for repeated synchronisations. When combined with SSH as its transport, every byte travels encrypted.

This guide covers practical rsync usage over SSH: common flag combinations, excluding files, bandwidth limiting, incremental backups with hard links, and cron-based automation.

Part of the VPN and SSH guide series. See also: SSH Tunneling | SSHFS and Remote File Access | WireGuard Setup


Basic Usage

rsync -avz -e ssh /local/path/ user@remote:/remote/path/

Flag breakdown

Flag Meaning
-a Archive mode: preserves permissions, ownership, timestamps, symlinks, and recurses into directories. Equivalent to -rlptgoD.
-v Verbose output.
-z Compress data during transfer (useful over slow links).
-e ssh Use SSH as the transport. This is the default on modern rsync but being explicit avoids surprises.

Trailing slash matters. rsync /src/ dest/ copies the contents of src into dest. rsync /src dest/ copies the directory itself, creating dest/src/.

Using a Non-Standard SSH Port or Key

rsync -avz -e "ssh -p 2222 -i ~/.ssh/deploy_key" /data/ user@host:/backup/

The -e flag accepts any SSH command string, so you can pass options like port, identity file, or cipher.

Mirroring with --delete

To make the destination an exact copy of the source, including removing files that no longer exist at the source:

rsync -avz --delete -e ssh /local/site/ user@web:/var/www/html/

Caution: --delete is destructive on the destination side. Always do a dry run first:

rsync -avzn --delete -e ssh /local/site/ user@web:/var/www/html/

-n (or --dry-run) shows what would happen without changing anything.

Excluding Files and Directories

Inline excludes

rsync -avz --exclude='.git' --exclude='node_modules' --exclude='*.log' \
    /project/ user@remote:/project/

Exclude file

For complex exclusion patterns, use a file:

cat > /etc/rsync-exclude.txt <<'EOF'
.git
node_modules
*.log
*.tmp
__pycache__
.env
EOF

rsync -avz --exclude-from=/etc/rsync-exclude.txt /project/ user@remote:/project/

Include + exclude combo

Sync only .conf files, excluding everything else:

rsync -avz --include='*.conf' --exclude='*' /etc/ user@remote:/backup/etc/

Order matters: includes are evaluated before excludes.

Bandwidth Limiting (--bwlimit)

Limit throughput to avoid saturating a slow link:

# Limit to 5000 KBps (~40 Mbps)
rsync -avz --bwlimit=5000 -e ssh /data/ user@remote:/data/

The value is in kilobytes per second. This is invaluable when syncing large datasets over shared production links during business hours.

The --link-dest option creates incremental backups that look like full backups (every file is present) but unchanged files are hard-linked to the previous snapshot, consuming no additional disk space.

#!/bin/bash
# incremental-backup.sh

REMOTE="user@remote:/data/"
BACKUP_DIR="/backup"
DATE=$(date +%Y-%m-%d_%H%M)
LATEST="$BACKUP_DIR/latest"

rsync -avz --delete \
    --link-dest="$LATEST" \
    -e ssh "$REMOTE" "$BACKUP_DIR/$DATE/"

# Update the 'latest' symlink
ln -snf "$BACKUP_DIR/$DATE" "$LATEST"

After several runs the backup directory looks like:

/backup/
    2026-04-14_0200/    # full snapshot (hard links where unchanged)
    2026-04-15_0200/
    2026-04-16_0200/
    latest -> 2026-04-16_0200

Each snapshot is a complete browsable tree, but disk usage reflects only the actual changes.

Resuming Interrupted Transfers

For large files, use --partial (or -P which combines --partial and --progress):

rsync -avzP -e ssh bigfile.tar.gz user@remote:/data/

If the transfer is interrupted, the partial file is kept on the destination. Rerunning the same command resumes from where it left off.

Automation with cron

Create a cron job for nightly backups:

crontab -e
# Nightly rsync backup at 02:00
0 2 * * * /usr/local/bin/incremental-backup.sh >> /var/log/rsync-backup.log 2>&1

Prerequisites for unattended rsync

  1. SSH key authentication -- no passphrase, or use ssh-agent.
  2. StrictHostKeyChecking=accept-new on first connection to auto-accept the host key (or pre-populate known_hosts).
  3. Restrictive authorized_keys on the remote side:
command="/usr/bin/rsync --server --sender -logDtprze.iLsfxCIvu . /data/",no-pty,no-agent-forwarding,no-port-forwarding ssh-ed25519 AAAA... backup@machine

This limits the key to running only the specific rsync command.

Comparing rsync with Alternatives

Tool Encryption Delta sync Resume Bi-directional
rsync + SSH Yes (SSH) Yes Yes (--partial) No (use Unison)
scp Yes (SSH) No No No
sftp Yes (SSH) No Yes No
rclone Yes (varies) Partial Yes Yes (some backends)

rsync's delta algorithm and hard-link snapshots make it unbeatable for server-to-server backup and deployment workflows.

Performance Tips

  • Disable compression on fast LANs: drop -z to avoid wasting CPU cycles when bandwidth is plentiful.
  • Use --compress-level=1 for a fast compression ratio on moderate links.
  • -W (whole file): skip the delta algorithm for local-to-local copies (rsync enables this automatically when source and destination are both local).
  • --info=progress2 shows a single overall progress line instead of per-file output, which is cleaner for large transfers.
rsync -av --info=progress2 -e ssh /data/ user@remote:/data/

Return to the VPN and SSH hub for more guides, or continue with SSHFS and Remote File Access.