rsync & scp — Copy files between servers with metadata, logging & resume support ---
rsync — recommended for most use cases
rsync is the go-to tool for server-to-server transfers. It supports incremental sync, preserves metadata, logs output, and can resume interrupted transfers.
Basic syntax
rsync [options] [user@]source:/path/ [user@]dest:/path/
Common option flags
| Flag | Meaning |
|---|---|
-a |
Archive mode — preserves permissions, timestamps, symlinks, owner, group |
-v |
Verbose — shows files being transferred |
-z |
Compress data during transfer |
-P |
Combines --progress + --partial — shows progress and keeps partial files for resume |
--numeric-ids |
Don't map UID/GID to names — safer across different systems |
--rsync-path="sudo rsync" |
Run rsync as sudo on the remote side |
--log-file=/path/to/log |
Write output to a log file |
--checksum |
Skip based on checksum instead of file size/time (slower, more accurate) |
--bwlimit=KBPS |
Throttle bandwidth (e.g. --bwlimit=50000 for ~50 MB/s) |
--exclude='pattern' |
Exclude files or directories matching a pattern |
--delete |
Delete files on destination that no longer exist on source |
-n / --dry-run |
Simulate transfer without making changes — always test first |
A note on trailing slashes
The trailing slash on a source path is significant in rsync and a common source of mistakes.
# Copies the CONTENTS of /data/myapp/ into /mnt/backup/
rsync -a user@10.0.0.10:/data/myapp/ /mnt/backup/
# Copies the DIRECTORY /data/myapp itself into /mnt/backup/
# Result: /mnt/backup/myapp/
rsync -a user@10.0.0.10:/data/myapp /mnt/backup/
Always double-check your trailing slashes before running, especially with --delete.
Example 1 — Remote to local, full metadata, with resume & log
rsync -avzP \
--numeric-ids \
--rsync-path="sudo rsync" \
user@10.0.0.10:/data/myapp/ \
/mnt/backup/myapp/ \
--log-file=/var/log/rsync_myapp.log
Example 2 — Local to remote
rsync -avzP \
--numeric-ids \
/mnt/backup/myapp/ \
user@10.0.0.10:/data/myapp/ \
--log-file=/var/log/rsync_push.log
Example 3 — Add checksum verification + bandwidth limit + exclude pattern
rsync -avzP \
--numeric-ids \
--checksum \
--bwlimit=50000 \
--exclude='*.tmp' \
--exclude='.cache/' \
user@10.0.0.10:/data/myapp/ \
/mnt/backup/myapp/ \
--log-file=/var/log/rsync_myapp.log
Example 4 — Dry run first (always recommended)
rsync -avzPn \
--numeric-ids \
user@10.0.0.10:/data/myapp/ \
/mnt/backup/myapp/ \
--log-file=/var/log/rsync_dryrun.log
Remove the -n flag once you're happy with the output.
Example 5 — Delete files on destination that no longer exist on source
rsync -avzP \
--numeric-ids \
--delete \
user@10.0.0.10:/data/myapp/ \
/mnt/backup/myapp/ \
--log-file=/var/log/rsync_myapp.log
⚠️ Use --delete with caution. Always do a dry run first.scp — simple alternative for one-off copies
scp is simpler but has no resume support and fewer options for metadata control. Use it for quick one-off file copies where rsync is overkill.
# Copy a file from remote to local
scp user@10.0.0.10:/data/myapp/archive.tar.gz /mnt/backup/
# Copy an entire directory recursively
scp -rp user@10.0.0.10:/data/myapp/ /mnt/backup/myapp/
| Flag | Meaning |
|---|---|
-r |
Recursive (directories) |
-p |
Preserve timestamps and permissions |
-C |
Enable compression |
-P 2222 |
Specify a custom SSH port |
-i ~/.ssh/id_rsa |
Use a specific SSH key |
Note: scp does not support resume. If the connection drops mid-transfer, you start over. Prefer rsync for large transfers.Re-running rsync after an interruption
Because of --partial (included in -P), rsync saves partially transferred files and picks up where it left off. Simply re-run the exact same command — rsync will skip already-completed files and resume any partial ones.
Tailing the log in real time
tail -f /var/log/rsync_myapp.log