Secure File Transfer with scp, sftp, and rsync
Select and operate the right SSH-based transfer tool, preserve the intended metadata, understand path and trailing-slash semantics, verify results, and prevent destructive synchronization mistakes.
Learning objectives
By the end of this lesson
- Choose among
scp, interactive or batchsftp, andrsync. - Understand local/remote path syntax and modern
scpprotocol behavior. - Use dry runs, itemized changes, filters, and trailing-slash semantics with
rsync. - Preserve only the metadata the workflow actually requires.
- Verify transferred content and design safe deletion or mirroring procedures.
1. Transfer requirements determine the tool
All three tools can use SSH transport, but they provide different operating models. scp is convenient for direct copies. Modern OpenSSH scp uses the SFTP protocol by default while retaining familiar source/destination syntax. sftp exposes a file-transfer protocol with interactive and batch commands. rsync compares file trees, transfers changed data, and can preserve selected metadata efficiently.
flowchart TB
A["What is the transfer requirement?"] --> B{"One direct copy?"}
B -->|Yes| C["scp"]
B -->|No| D{"Interactive or scripted remote file operations?"}
D -->|Yes| E["sftp batch or interactive mode"]
D -->|No| F{"Tree synchronization, resume, filters, or mirroring?"}
F -->|Yes| G["rsync over SSH"]
C --> H["Verify destination content and metadata"]
E --> H
G --> H
H --> I["Record evidence and cleanup"]SSH can protect data in transit while an incorrect source path, destination path, delete option, ownership setting, or trailing slash still produces the wrong result.
2. scp provides concise copy semantics
# Local file to a remote directory
scp ./release.tar.gz app-prod:/srv/releases/
# Remote file to the current local directory
scp app-prod:/var/log/app/health.json ./
# Recursive directory copy
scp -r ./static/ app-prod:/srv/www/
# Preserve modification times and modes
scp -p ./release.tar.gz app-prod:/srv/releases/
# Limit bandwidth in Kbit/s and enable compression for compressible data
scp -l 20000 -C ./large-text-dump.sql app-prod:/srv/staging/
# Modern scp uses SFTP by default. -O requests the legacy SCP protocol
# only when an old server requires it and the risk is understood.
# scp -O ./file legacy-host:/tmp/Remote operands use [user@]host:path. Quote shell metacharacters deliberately because local and remote path interpretation can differ by tool and protocol mode. Test unusual names, spaces, wildcards, and leading hyphens in a disposable directory before automating them.
3. sftp supports controlled remote file operations
SFTP is a protocol carried through SSH, not “FTP with encryption.” It supports operations such as listing, upload, download, rename, create directory, remove, and permission changes according to server capabilities and policy. Batch mode makes operations reviewable and fails when a command fails unless the command is prefixed to ignore errors.
# Interactive session
sftp app-prod
# Example interactive commands:
# pwd
# lpwd
# ls -la
# lls -la
# put release.tar.gz /srv/releases/release.tar.gz.part
# rename /srv/releases/release.tar.gz.part /srv/releases/release.tar.gz
# get /var/log/app/health.json ./health.json
# bye
# Batch file for repeatable deployment
cat > deploy.sftp <<'EOF'
-mkdir /srv/releases/incoming
put release.tar.gz /srv/releases/incoming/release.tar.gz.part
rename /srv/releases/incoming/release.tar.gz.part /srv/releases/incoming/release.tar.gz
ls -l /srv/releases/incoming/release.tar.gz
EOF
sftp -b deploy.sftp app-prodUpload to a temporary name and rename only after successful completion when consumers watch the destination directory. Rename behavior and atomicity depend on server and filesystem context, so validate the target platform.
4. rsync synchronizes trees and selected attributes
Rsync uses a quick check based primarily on size and modification time unless options request other behavior. Over a remote shell, rsync normally starts a remote rsync process through SSH, so rsync must be installed on both ends. Archive mode -a is a bundle of options; it is not “preserve absolutely everything.” ACLs, extended attributes, hard links, sparse files, numeric IDs, and ownership may need additional options and privileges.
# Always preview a meaningful synchronization first
rsync -aivn --delete-delay \
--exclude '.git/' \
./site/ app-prod:/srv/www/site/
# Apply after reviewing the itemized dry run
rsync -aiv --delete-delay \
--exclude '.git/' \
./site/ app-prod:/srv/www/site/
# Resume partial large transfers in a controlled directory
rsync -aiv --partial --partial-dir=.rsync-partial \
./artifacts/ app-prod:/srv/artifacts/
# Preserve ACLs and xattrs when both endpoints and privileges support them
rsync -aivAX ./tree/ app-prod:/srv/tree/
# Use a specific SSH profile or option set
rsync -aiv -e 'ssh -o IdentitiesOnly=yes' \
./reports/ app-prod:/srv/reports/--delete mirrors absenceIt can remove destination files that are not present in the source set. Use a dry run, understand filter interactions, protect required paths, preserve backups or snapshots, and verify that source and destination are not reversed.
5. Trailing slashes change directory semantics
With rsync, source and source/ are not equivalent. A source directory without a trailing slash transfers the directory itself into the destination. A trailing slash transfers the directory’s contents. This single character is one of the most common causes of unexpected nested paths.
mkdir -p demo/source/sub demo/dest-a demo/dest-b
printf 'alpha\n' > demo/source/file.txt
printf 'beta\n' > demo/source/sub/nested.txt
# Creates demo/dest-a/source/...
rsync -a demo/source demo/dest-a/
# Creates contents directly under demo/dest-b/...
rsync -a demo/source/ demo/dest-b/
find demo -type f -printf '%P\n' | sortDestination trailing slashes can also affect interpretation when the target does not exist. Write test cases for the exact production path layout instead of relying on memory.
6. Verification must match the risk
A zero exit status proves the tool completed according to its own checks, not that the business outcome is correct. Verify file count, expected paths, sizes, checksums where appropriate, ownership, permissions, ACLs, extended attributes, symlink targets, and application readability.
# Build a source manifest before transfer
find ./release -type f -print0 | sort -z | xargs -0 sha256sum > source.sha256
# On the destination, create the same relative manifest
ssh app-prod '
cd /srv/releases/current &&
find . -type f -print0 | sort -z | xargs -0 sha256sum
' > destination.sha256
# Compare manifests after normalizing source path prefixes if needed
diff -u source.sha256 destination.sha256
# Metadata-oriented inspection
find ./release -printf '%M %u:%g %s %TY-%Tm-%TdT%TH:%TM:%TS %P\n' | sort > source-metadata.txt
ssh app-prod "find /srv/releases/current -printf '%M %u:%g %s %TY-%Tm-%TdT%TH:%TM:%TS %P\\n' | sort" \
> destination-metadata.txtChecksumming every file increases I/O and may not be necessary for routine synchronization over authenticated transport, but it is useful for release artifacts, evidence preservation, or suspected corruption. Choose verification proportional to consequences.
7. Hands-on lab: model a safe local rsync release
This lab uses local directories to teach the same planning and verification steps without requiring a remote server.
lab="$HOME/devops-academy/linux/chapter13/lesson04"
rm -rf "$lab"
mkdir -p "$lab/source/assets" "$lab/destination" "$lab/backups"
cd "$lab"
printf 'version=1\n' > source/app.conf
printf 'console.log("v1");\n' > source/assets/app.js
printf 'remove me\n' > destination/obsolete.txt
# Capture the proposed changes; do not apply yet
rsync -aivn --delete-delay --backup --backup-dir="$lab/backups" \
source/ destination/ > dry-run.txt
cat dry-run.txt
# Apply the reviewed plan
rsync -aiv --delete-delay --backup --backup-dir="$lab/backups" \
source/ destination/ > applied.txt
# Verify content and demonstrate where deleted/replaced files were retained
find source -type f -print0 | sort -z | xargs -0 sha256sum > source.sha256
find destination -type f -print0 | sort -z | xargs -0 sha256sum > destination.sha256
sed 's# source/# destination/#' source.sha256 > expected-destination.sha256
diff -u expected-destination.sha256 destination.sha256
test -f backups/obsolete.txt
find . -maxdepth 3 -type f -printf '%P\n' | sort > inventory.txt
sha256sum dry-run.txt applied.txt source.sha256 destination.sha256 inventory.txt > evidence.sha256Verification checklist
8. Common secure-transfer mistakes
“SSH encryption guarantees the correct destination tree.”
It protects transport, not path intent, delete behavior, ownership, or application semantics.
“Archive mode preserves everything.”
-a omits some attributes such as ACLs, xattrs, and hard links unless additional options are selected.
“The trailing slash is cosmetic.”
For rsync it changes whether the source directory itself or only its contents are transferred.
“A dry run makes --delete safe forever.”
The source and destination can change between preview and execution. Minimize the gap and retain rollback.
9. Knowledge check
Question 1. What protocol does modern OpenSSH scp use by default?
-O option requests the legacy SCP protocol when required.Question 2. What is the difference between rsync source destination/ and rsync source/ destination/?
Question 3. Why should an rsync mirror use both a dry run and rollback protection?
10. Summary
Secure transfer requires more than encrypted transport. Choose scp for direct copies, SFTP for file-operation workflows, and rsync for controlled synchronization. Make paths and trailing slashes explicit, preview destructive actions, preserve only required metadata, use temporary names where consumers observe files, and verify content and operational behavior after transfer.
11. Further reading
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.