Skip to content

SCP

SCP (Secure Copy Protocol) transfers files between hosts over SSH.

Syntax

scp [options] source destination

Remote paths take the form user@host:/path/to/file.

Copy files

Local to remote

# single file
scp file.txt user@192.168.1.10:/home/user/

# with non-default SSH port
scp -P 2222 file.txt user@host:/tmp/

# preserve timestamps and permissions
scp -p file.txt user@host:/tmp/

# with compression (useful over slow links)
scp -C archive.tar.gz user@host:/backups/

Remote to local

scp user@host:/var/log/app.log ./app.log
scp user@host:/etc/nginx/nginx.conf ./nginx.conf.bak

Remote to remote

scp user1@host1:/data/file.csv user2@host2:/data/file.csv

Directories

# recursive copy
scp -r ./dist/ user@host:/var/www/html/

# remote to local
scp -r user@host:/var/www/html/ ./backup/

Key-based authentication

# specify identity file
scp -i ~/.ssh/id_rsa file.txt user@host:/tmp/

# specify identity file and port
scp -i ~/.ssh/deploy_key -P 2222 file.txt user@host:/tmp/

Jump hosts (ProxyJump)

When the target is not directly reachable:

# hop through bastion to reach internal host
scp -J bastion.example.com file.txt user@internal-host:/tmp/

# multi-hop
scp -J user@bastion:22,user@hop2:22 file.txt user@target:/tmp/

Bandwidth limiting

# limit to 8000 Kbit/s (~1 MB/s)
scp -l 8000 largefile.tar.gz user@host:/backups/

Common options

Option Description
-r Recursive (directories)
-P port SSH port (capital P)
-i keyfile Identity file
-p Preserve file timestamps and mode
-C Enable compression
-l limit Bandwidth limit in Kbit/s
-q Quiet — suppress progress
-v Verbose — debug output
-J host ProxyJump through host
-o option Pass SSH option (e.g. -o StrictHostKeyChecking=no)

SSH config integration

Options in ~/.ssh/config apply to SCP too:

Host bastion
  HostName bastion.example.com
  User deploy
  IdentityFile ~/.ssh/deploy_key
  Port 22

Host internal
  HostName 10.0.1.50
  User app
  ProxyJump bastion
# once the config is set, this just works
scp file.txt internal:/app/uploads/

Alternatives

rsync is generally preferred for large transfers or repeated syncs:

# rsync over SSH — only transfers changed blocks
rsync -avz -e "ssh -i ~/.ssh/id_rsa" ./dist/ user@host:/var/www/html/

# dry run
rsync -avzn ./dist/ user@host:/var/www/html/