Skip to content

Rsync over SSH

Overview

Rsync is the most efficient tool for file synchronization and transfer. When used with SSH, it provides:

  • Delta transfers (only changed parts)
  • Resume support
  • Compression
  • Preservation of permissions, ownership, timestamps
  • Bandwidth limiting
┌──────────────────────────────────────────────────────────────────────────┐
│                         Rsync Delta Transfer                              │
│                                                                           │
│   Source                                     Destination                  │
│   ┌─────────────────┐                       ┌─────────────────┐          │
│   │ File: 100 MB    │                       │ File: 100 MB    │          │
│   │                 │    Only Changed       │ (older version) │          │
│   │ ████████░░░░░░░ │───────────────────────>│ ████████████████ │          │
│   │                 │    Blocks (~1 MB)     │                 │          │
│   │ (10% changed)   │                       │ (updated)       │          │
│   └─────────────────┘                       └─────────────────┘          │
│                                                                           │
│   Traditional copy: 100 MB                                               │
│   Rsync delta:      ~10 MB                                               │
│                                                                           │
└──────────────────────────────────────────────────────────────────────────┘

Basic Syntax

rsync [options] source destination

Common Options

Option Description
-a Archive mode (preserves everything)
-v Verbose
-z Compress during transfer
-P Progress + partial (resume)
--delete Delete extraneous files from dest
-n Dry run (show what would happen)
-e ssh Use SSH (default on modern rsync)

Basic Transfers

Local to Remote

rsync -avz /local/path/ user@host:/remote/path/

Remote to Local

rsync -avz user@host:/remote/path/ /local/path/

With Progress

rsync -avzP /local/path/ user@host:/remote/path/

Important: Trailing Slashes

# With trailing slash: copy CONTENTS of dir
rsync -av /source/ /dest/
# Result: /dest/file1, /dest/file2

# Without trailing slash: copy dir ITSELF
rsync -av /source /dest/
# Result: /dest/source/file1, /dest/source/file2

SSH Options

Custom Port

rsync -avz -e "ssh -p 2222" /local/ user@host:/remote/

Specific Key

rsync -avz -e "ssh -i ~/.ssh/mykey" /local/ user@host:/remote/

Through Jump Host

rsync -avz -e "ssh -J jumphost" /local/ user@internal:/remote/

Multiple SSH Options

rsync -avz -e "ssh -p 2222 -i ~/.ssh/key -o StrictHostKeyChecking=no" /local/ user@host:/remote/

Synchronization

Mirror (Delete Extra Files)

rsync -avz --delete /local/ user@host:/remote/

Dangerous

--delete removes files from destination that don't exist in source. Use with caution.

Dry Run First

rsync -avzn --delete /local/ user@host:/remote/
# Shows what WOULD happen without doing it

One-Way Sync

rsync -avz --delete /source/ /dest/

Exclude Files

rsync -avz --exclude='*.log' --exclude='cache/' /local/ user@host:/remote/

Exclude from File

rsync -avz --exclude-from='exclude.txt' /local/ user@host:/remote/
# exclude.txt
*.log
*.tmp
cache/
.git/
node_modules/

Include/Exclude Patterns

# Only sync *.php and *.html
rsync -avz --include='*.php' --include='*.html' --exclude='*' /local/ user@host:/remote/

Resume Transfers

Partial Files

rsync -avzP /local/largefile.iso user@host:/remote/
# -P = --partial --progress
# If interrupted, run same command to resume

Partial Directory

rsync -avz --partial-dir=.rsync-partial /local/ user@host:/remote/

Bandwidth Control

Limit Speed

rsync -avz --bwlimit=1000 /local/ user@host:/remote/
# Limit in KB/s (1000 KB/s = ~1 MB/s)

Preserve Options

Archive Mode (-a)

Equivalent to: -rlptgoD - -r Recursive - -l Preserve symlinks - -p Preserve permissions - -t Preserve times - -g Preserve group - -o Preserve owner - -D Preserve devices and specials

Additional Preservation

rsync -avzP --acls --xattrs /local/ user@host:/remote/
# Also preserve ACLs and extended attributes
rsync -avzH /local/ user@host:/remote/
# -H preserves hard links

Comparison Options

Skip Based on Checksum

rsync -avzc /local/ user@host:/remote/
# -c uses checksum instead of time/size (slower but accurate)

Update Only (Skip Newer)

rsync -avzu /local/ user@host:/remote/
# -u skip files that are newer on receiver

Ignore Existing

rsync -avz --ignore-existing /local/ user@host:/remote/

Backup Strategies

Simple Backup

rsync -avz /data/ user@backup:/backups/$(date +%Y%m%d)/
rsync -avz --link-dest=/backups/latest /data/ user@backup:/backups/$(date +%Y%m%d)/
ln -sfn /backups/$(date +%Y%m%d) /backups/latest

Space-efficient: unchanged files are hard-linked.

Backup with Rotation

#!/bin/bash
# backup.sh

DEST="user@backup:/backups"
LINK_DEST="--link-dest=../latest"

# Rotate
ssh user@backup "rm -rf /backups/backup.3"
ssh user@backup "mv /backups/backup.2 /backups/backup.3 2>/dev/null"
ssh user@backup "mv /backups/backup.1 /backups/backup.2 2>/dev/null"
ssh user@backup "mv /backups/latest /backups/backup.1 2>/dev/null"

# New backup
rsync -avz --delete $LINK_DEST /data/ $DEST/latest/

Logging

Verbose Output to File

rsync -avz /local/ user@host:/remote/ | tee rsync.log

Log File Option

rsync -avz --log-file=rsync.log /local/ user@host:/remote/

Statistics

rsync -avz --stats /local/ user@host:/remote/

Common Use Cases

Website Deployment

rsync -avz --delete \
    --exclude='.git' \
    --exclude='node_modules' \
    --exclude='.env' \
    /local/project/ user@web:/var/www/site/

Database Backup Sync

rsync -avzP \
    --bwlimit=5000 \
    /var/backups/mysql/ user@backup:/backups/mysql/

Photo/Media Sync

rsync -avzP \
    --include='*.jpg' \
    --include='*.mp4' \
    --include='*/' \
    --exclude='*' \
    /media/photos/ user@nas:/photos/

Home Directory Backup

rsync -avz \
    --exclude='.cache' \
    --exclude='Downloads' \
    --exclude='.local/share/Trash' \
    ~/ user@backup:/backups/home/

Troubleshooting

Permission Errors

# Check user has write permission
ssh user@host "ls -la /remote/path"

# Or use --no-perms
rsync -avz --no-perms /local/ user@host:/remote/

Slow Initial Scan

# Disable delta for first sync of large files
rsync -avz --whole-file /local/ user@host:/remote/

Connection Timeout

rsync -avz -e "ssh -o ServerAliveInterval=60" /local/ user@host:/remote/

Character Encoding

rsync -avz --iconv=UTF-8,UTF-8 /local/ user@host:/remote/

Debugging

rsync -avvvz /local/ user@host:/remote/
# Extra v's for more detail

Rsync Daemon Mode

For better performance (no SSH overhead):

Server Setup

# /etc/rsyncd.conf
[backup]
    path = /data/backup
    read only = no
    auth users = backupuser
    secrets file = /etc/rsyncd.secrets
# /etc/rsyncd.secrets
backupuser:secretpassword

Client Usage

rsync -avz /local/ backupuser@host::backup/

Security

Rsync daemon mode isn't encrypted. Use SSH for security, or only on trusted networks.

Comparison

Feature rsync scp sftp
Delta transfer Yes No No
Resume Yes No Yes
Progress Yes Yes Yes
Sync/delete Yes No No
Bandwidth limit Yes Yes Yes
Preserve perms Yes Yes Yes
Complexity Medium Low Low