kmail.at
contact

elsewhere

EnglishEN
← learning

networking · difficulty ◆◆

scp — copy files securely between machines

Encrypt a copy of your file to another machine in one command.

You have a file on one box and you need it on another — and you want it encrypted in transit, not pasted through a chat log. scp is the shortest answer in Unix, and its flag order catches out almost everyone the first time.

AI-generated draft · reviewed and editedThis text was generated with AI as a first draft, then reviewed and edited by the author, who holds editorial responsibility for it.

2026-09-13 · 5 min read

$ scp

What it does

scp (secure copy) copies files or directories between hosts using SSH for transport and encryption. The syntax follows cp but one side is a remote path written as [user@]host:/path. `scp file.txt me@server:/tmp/` pushes the file over the encrypted SSH channel to /tmp/ on server. Reverse the argument order and it pulls instead. Because it rides on SSH you get encryption, integrity checking, and the same key-based authentication you already use, with no extra setup beyond an SSH daemon you probably already run — compression is available with -C and agent forwarding with -A, but neither is on by default.

Why it matters

When you are knee-deep in a deployed app, moving an artifact between machines is a daily event: ship a build to prod, pull a log for inspection, drop a config onto a box. scp does it with one command and the encryption is non-negotiable — the data never crosses the wire in plaintext. It is simpler than rsync for one-off pushes (no flag soup), and once it clicks you stop scp-ing like a beginner, which is honestly rare: most people get the source/destination order wrong on the first try and it bites them silently.

What the 2022 SFTP switch changed for you

Two everyday habits changed when OpenSSH 9.0 moved scp onto SFTP by default. Remote wildcards are gone: `scp host:/var/log/*.log .` used to be expanded by the remote shell, and with SFTP there is no shell to expand it, so the transfer fails rather than matching files — copy the directory, or list the files first. Quoting got simpler in the opposite direction: spaces and quotes in filenames no longer need the double-quoting the legacy protocol demanded, and getting that quoting wrong used to run commands on the remote host. If a very old server cannot speak SFTP, -O forces the legacy protocol back on.

Example

$ scp build.tar.gz deploy@web-01:/var/www/releases/
build.tar.gz                                    100%  128MB  45.6MB/s   00:03

Push build.tar.gz from the local box to /var/www/releases/ on web-01 over SSH. The progress line is printed to stderr and takes the form name / percentage / size / rate / elapsed — a real transfer here reads 'src.bin 100% 293KB 78.6MB/s 00:00' — and the data is encrypted end to end.

$ scp -r ./dist app@staging:/srv/app/
public.css                                   100%   12KB   8.2MB/s   00:00
index.html                                   100%   18KB  11.9MB/s   00:00
main.js                                      100%  342KB  22.1MB/s   00:00
assets/logo.png                              100%   45KB  15.3MB/s   00:00

-r recursively copies the whole ./dist directory tree to /srv/app/ on staging. Without -r, scp refuses to copy a directory.

$ scp db@db-01:/var/log/mysql/error.log .
error.log                                     100%   26KB  14.9MB/s   00:00

Reverse the arguments and scp pulls the file from db-01 down to the current directory. The remote path comes first now — a common source of confusion.

$ scp -P 2222 -C config.toml deploy@bastion:/etc/app/
config.toml                                   100%   38KB  18.3MB/s   00:00

-P sets a non-default SSH port (2222 here) and -C enables compression, handy for slow links and text-heavy config files.

Common flags

-r
recursively copy an entire directory tree
-P <port>
SSH port to connect to on the remote host (capital P, unlike ssh)
-p
preserve modification times, access times and file mode bits
-C
enable compression on the transfer (passes -C to ssh)
-i <identity_file>
use a specific private key for public-key auth
-l <limit>
limit bandwidth used by the transfer, e.g. -l 800 to cap at 100 KB/s
-q
quiet mode — suppress the progress meter and warnings

History

Origin

scp came with Tatu Ylonen's original SSH: the source file credits '1995 Timo Rinne, Tatu Ylonen' for the secure remote copy, and the OpenSSH version you run today describes itself as 'basically patched BSD rcp which uses ssh to do the data transfer (instead of using rcmd)' — carrying 1999 copyrights from Theo de Raadt and Aaron Campbell. It copied rcp's interface so users could switch without relearning anything, but tunneled the data over the SSH protocol, which gave it encryption and integrity checks. OpenSSH 1.2.2 — the first release, built from Ylönen's ssh 1.2.12 — shipped with OpenBSD 2.6 on 1 December 1999, and scp has been a standard way to move a file between Unix machines ever since.

Why the modern rewrite

Because the original scp predates SSH's SFTP protocol and speaks the original SCP protocol, OpenSSH 9.0 (April 2022) switched scp to use SFTP by default and added a -O flag to force the old mode for ancient servers. The documented reason was not speed: legacy scp/rcp expands remote filenames through the remote shell, so filenames on the command line had to be double-quoted or they could be interpreted as commands on the far side. Under SFTP there is no shell to expand anything and no quoting to get wrong. The flag quirks survive too: the port is -P (capital) in scp but -p (lowercase) in ssh, and scp's -p means preserve — a trap that has burned everyone at least once.

Fun facts

Pros & cons

pros

  • + Zero setup if SSH already works — no FTP/HTTP server to install
  • + Encrypted end to end with integrity verification built in
  • + Copies files, directories, and works through standard SSH auth (keys, agent)
  • + Universal: ships with OpenSSH on every Unix-like system

cons

  • − Single-shot copy — no delta sync, resume, or checksum compare like rsync
  • − Slow on large trees or slow links unless you add -C compression
  • − No remote wildcard expansion at all under the SFTP default (the legacy -O mode expands them through the remote shell, with the quoting hazards that brings)
  • − OpenSSH 9.x+ dropped the legacy protocol by default; ancient servers need -O

Takeaways

  1. 1Push: `scp file user@host:/dest/` — remote path on the right
  2. 2Pull: `scp user@host:/remote/file .` — remote path on the left, dot for the current dir
  3. 3Recursive: add `-r` for a directory, or it will refuse
  4. 4Port and preserve flags are flipped from ssh: -P is the port, -p is preserve
  5. 5One-off copy is scp; ongoing or resumable sync is rsync -avz

Related commands