scp, sftp and rsync over SSH

SSH · Practical · 10 min

Moving builds, logs and backups between machines is a constant task, and the right tool saves time and bandwidth.

Three tools copy files over SSH, and all of them use your ~/.ssh/config aliases and keys.

scp copies like cp, with host:path for remote locations: scp app.tgz web:/tmp/, scp web:/var/log/app.log ., -r for directories, -P (capital) for the port. Since OpenSSH 9.0, scp uses the SFTP protocol underneath, which fixed old quoting and security problems with remote paths.

sftp is an interactive session (ls, cd, get, put, lcd for the local side) and can be scripted with -b batchfile. It is also what many GUI clients speak.

rsync (a separate program, which must be installed on both ends) is the best choice for directories and repeated copies: it transfers only the differences, can resume, and preserves permissions and times with -a.

  • rsync -avz src/ web:/srv/app/ syncs the contents of src into /srv/app.
  • Without the trailing slash, src itself is created inside the target. This trailing-slash rule is the classic rsync gotcha.
  • --delete removes files on the target that no longer exist in the source; preview with -n (dry run) first.
  • -e 'ssh -p 2222' passes ssh options.

For a quick one-off file, scp is fine; for anything recurring or large, use rsync.

Example

scp app.tgz web:/tmp/                  # local -> remote
scp web:/var/log/app.log ./logs/       # remote -> local
scp -r -P 2222 dist/ deploy@host:/srv/ # directory, custom port

sftp web
# sftp> get /var/log/app.log
# sftp> put build.zip /tmp/

rsync -avz --delete -n dist/ web:/srv/app/   # dry run: what would change
rsync -avz --delete dist/ web:/srv/app/      # sync contents of dist/
rsync -avz -e 'ssh -p 2222' data/ backup:/data/

Remember

  • scp for quick copies, sftp for interactive sessions, rsync for directories
  • rsync src/ copies contents; rsync src copies the directory itself
  • Dry-run rsync --delete with -n before running it for real

Watch out for

  • Running rsync --delete with the wrong trailing slash or target; always dry-run first.

Check yourself

  1. What is the difference between `rsync -a src web:/dst/` and `rsync -a src/ web:/dst/`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: man.openbsd.org/scp