iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

scp / rsync

scp and rsync both copy files over SSH; rsync wins for anything more than a one-off because it transfers only differences, preserves metadata, resumes safely, and handles large trees efficiently. Use scp for a quick one-file copy; reach for rsync for backups, deploys, and any tree larger than a hundred files.

Common patterns: copy, sync, exclude, resume

EXAMPLE
# ===== scp — fine for one-off file copies =====

# Copy a file FROM your machine TO a server
scp ./backup.tar.gz user@host:/srv/backups/

# Copy FROM a server TO your machine
scp user@host:/var/log/app.log .

# Whole directory (recursive)
scp -r ./build user@host:/srv/app/current/

# Use a non-default key + port
scp -i ~/.ssh/deploy_key -P 2222 file.txt user@host:/tmp/

# Resume? scp cannot resume an interrupted transfer. That is when rsync wins.

# ===== rsync — the tool you reach for after the first scp pain =====

# Basic mirror local -> remote with delta + preserve attrs (-a) + progress (-P)
rsync -aP ./build/ user@host:/srv/app/current/
# Trailing slash on the SOURCE means 'contents of'; without it copies the directory itself.

# Dry-run first when you are about to do anything sweeping
rsync -aPn --delete ./build/ user@host:/srv/app/current/   # -n = no changes, prints what would happen

# --delete removes files on the destination that no longer exist on the source.
# Pair with --dry-run until you are SURE.

# Exclude patterns (single + file-based)
rsync -aP --exclude='node_modules' --exclude='.git' ./ user@host:/srv/code/
rsync -aP --exclude-from=.rsyncignore ./ user@host:/srv/code/

# Resumable big-file transfer (continues on TCP drop)
rsync --append-verify --partial --progress big-image.iso user@host:/srv/

# Bandwidth-limit so you do not saturate the link (KB/s)
rsync -aP --bwlimit=5000 ./photos/ user@host:/backups/photos/

# Use a non-default key or port
rsync -aP -e 'ssh -i ~/.ssh/deploy_key -p 2222' ./build/ user@host:/srv/app/

# Sync IN BOTH DIRECTIONS is NOT what rsync does — use unison for that.

# ===== Common production patterns =====

# 1) Atomic-ish web deploy
ts=$(date +%Y%m%d-%H%M%S)
rsync -aP --delete ./build/ user@host:/srv/app/releases/$ts/
ssh user@host "ln -sfn /srv/app/releases/$ts /srv/app/current && systemctl reload nginx"

# 2) Nightly backup, rotated daily
rsync -aP --link-dest=/backups/prev/ /data/ /backups/today/
mv /backups/prev /backups/old-$(date +%F)
mv /backups/today /backups/prev
# Hard-linked snapshots take near-zero extra space when most files are unchanged.

# 3) Pull logs FROM many hosts
for host in web-1 web-2 web-3; do
  rsync -aP user@$host:/var/log/app/ /local/logs/$host/
done

# ===== Decision matrix =====
# - One file, one shot:         scp
# - Directory tree:             rsync -aP
# - Big or slow link:           rsync (delta + resume)
# - Repeating job:              rsync (idempotent + delta)
# - Destination mirror:         rsync -aP --delete (dry-run first!)
# - Atomic deploy:              rsync to releases/<ts>/ + symlink swap

Why it matters

`rsync -aPn` is the safe first command for any deploy or backup change. The `n` is dry-run — it prints exactly what would change without touching anything. The first time `--delete` is about to wipe a directory you forgot to exclude, the dry-run saves you.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
scp file.txt user@host:/tmp
rsync -avh src/ user@host:/srv/app/
Try it Yourself »

Discussion

Loading…