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.
Discussion
Loading…