opscape

Networking and remote

scp

Copy files between hosts over ssh, with cp-like syntax where a remote path is written HOST:PATH.

scp [OPTION...] SOURCE... TARGET     (either side may be [USER@]HOST:PATH)

9 of 9 examples run in Debian 13.7 on 2026-10-31 · openssh-client 1:10.0p1-7+deb13u4 · manual: scp(1)

scp is cp across machines: scp file host:dir/ uploads, scp host:file . downloads. It uses the same connection, keys and ~/.ssh/config as ssh, so if ssh host works, scp works too. A path after the colon is relative to your home directory on the remote host; start it with / for an absolute path.

scp always copies everything it is given. For repeated copies of a directory, or anything big enough that an interrupted transfer would hurt, rsync is better: it sends only what changed and can resume. For a quick one-off file, scp is the shortest command.

Since OpenSSH 9.0, scp talks the SFTP protocol under the hood instead of the old rcp-style protocol. You rarely notice, except that remote wildcards are expanded by scp itself rather than by the remote shell. -O switches back to the legacy protocol for very old servers. In these examples the remote host server is the sandbox itself, so remote paths land in /home/dev.

Examples

Copy a file to a remote host

scp todo.txt server:/tmp/
ssh server 'ls -l /tmp/todo.txt'
output
-rw-rw-r-- 1 dev dev 62 Oct 31 20:20 /tmp/todo.txt

server:/tmp/ is a directory on the remote host, so the file keeps its name there. scp prints nothing here because its output is not a terminal; interactively it shows a progress bar.

Copy a file from a remote host

scp server:demo/data/hosts.txt ./hosts-copy.txt
cat hosts-copy.txt
output
web01 10.0.0.11
web02 10.0.0.12
db01 10.0.0.21
cache01 10.0.0.31

demo/data/hosts.txt is relative to your home directory on the remote side. The local target gives the copy a new name.

Copy several files into a remote directory

Setup run before this example
ssh server 'mkdir -p uploads'
scp notes.txt todo.txt README.md server:uploads/
ssh server 'ls uploads'
output
README.md
notes.txt
todo.txt

As with cp, the last argument is the target and every other one is copied into it. The remote directory has to exist.

Copy a directory with -r

scp -r src server:src-copy
ssh server 'find src-copy -type f | sort'
output
src-copy/Makefile
src-copy/app.py
src-copy/lib/helpers.py
src-copy/main.c
src-copy/util.sh

-r copies the whole tree. Because src-copy did not exist, the copy of src is created under that name; had it existed, the tree would have gone inside it as src-copy/src.

Keep modification times with -p

scp access.log server:/tmp/plain.log
scp -p access.log server:/tmp/kept.log
ssh server 'ls -l --time-style=+%F /tmp/*.log'
output
-rw-rw-r-- 1 dev dev 2309 2026-10-24 /tmp/kept.log
-rw-rw-r-- 1 dev dev 2309 2026-10-31 /tmp/plain.log

A plain copy gets the current time; -p preserves the original modification time (and access time and mode), so the copy shows the same date as access.log.

Copy remote files matching a pattern

mkdir fetched
scp 'server:demo/logs/*.log' fetched/
ls fetched
output
app.log
error.log

Quote the remote pattern so your local shell leaves it alone; scp expands it on the remote side. app.log.1 does not end in .log and is skipped.

Use a different port with -P

scp -P 22 todo.txt server:/tmp/
echo "copied, exit $?"
output
copied, exit 0

scp's port option is a capital -P (lowercase -p means preserve). Port 22 is the default anyway; use this for servers listening elsewhere, or set Port in ~/.ssh/config.

Use a host alias from an ssh config

ssh_config
Host backup
  HostName 127.0.0.1
  User dev
scp -F ssh_config data/sales.csv backup:/tmp/
ssh server 'wc -l /tmp/sales.csv'
output
13 /tmp/sales.csv

Every setting from the ssh configuration applies to scp: here the alias backup resolves to an address and user. In everyday use the block lives in ~/.ssh/config and -F is not needed.

A remote file that does not exist

scp server:nope.txt .
explain
outputexit 1
scp: nope.txt: No such file or directory

scp reports the missing remote path and exits with 1. Nothing is created locally.

Common pitfalls

  • scp file host (no colon) makes a local copy named host. The colon is what makes a path remote.

  • -P is the port and -p is preserve, the opposite of ssh's -p port.

  • scp re-sends whole files every time and cannot resume. For large or repeated transfers use rsync -aP.

  • File names with spaces on the remote side: since OpenSSH 9.0 (SFTP mode) scp 'host:my file.txt' . works as written; with -O (legacy mode) the remote shell splits the name and you need extra quoting.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • All platforms use OpenSSH's scp. Versions before 9.0 (common on older servers and macOS releases) use the legacy protocol by default, where remote wildcards and quoting are handled by the remote shell.

Options worth knowing

A selection, described in our own words. For the full list, see scp(1) or scp --help.

OptionWhat it does
-rCopy directories recursively.
-pPreserve modification and access times and modes.
-P PORTConnect to PORT on the remote host.
-i FILEUse the private key in FILE.
-F FILEUse FILE as the ssh configuration.
-o OPTIONPass an ssh option, for example -o ConnectTimeout=5.
-qQuiet: no progress meter or warnings.
-vPrint ssh debug messages.
-CCompress the transfer.
-l KBITSLimit bandwidth in Kbit/s.
-J [USER@]HOST[:PORT]Connect through a jump host.
-3Copy between two remote hosts through the local machine.
-OUse the legacy SCP protocol instead of SFTP.
-sUse the SFTP protocol (the default in OpenSSH 9.0 and later).
-4Use IPv4 only.
-6Use IPv6 only.

Related