When you deploy a configuration to a server, fetch logs or move a file quickly between two machines, scp (Secure Copy Protocol) is the most direct tool. It relies on SSH to encrypt the transfer end to end: a single command, no FTP server to set up. For one-off transfers, scp remains unbeatable in simplicity.
What you will learn
- Copy a file to or from a remote server
- Transfer a complete directory with
-r - Use a custom SSH port with
-P(uppercase) - Know the difference between
-pand-P - Recognise when to prefer rsync over scp
Where scp fits among Linux transfer tools
scp belongs to the file transfer tools of Linux. Each of them has its speciality, and the first table places them side by side. The second one answers the question you actually ask in front of a terminal: for this situation, which tool should I reach for? Read it as a decision grid, where scp wins every time the transfer is single and immediate.
| Command | Speciality | When to use it |
|---|---|---|
scp | Remote copy over SSH | Quick one-shot transfer |
rsync | Smart synchronisation (delta) | Backups, large files, resume |
sftp | Interactive transfer over SSH | Remote browsing, long sessions |
tar + ssh | Archive + transfer | Trees with complex permissions |
curl / wget | HTTP/FTP download | Fetch from the web |
When to use scp rather than the others:
| Situation | Best tool | Why |
|---|---|---|
| Copy a single file to a server | scp | Simpler than rsync for one file |
| Back up a directory regularly | rsync | Only transfers the differences |
| Transfer cut halfway | rsync -P | scp starts over from zero |
| Transfer a very large file | rsync | Resume possible, controlled bandwidth |
| Fetch a log quickly | scp | A single command, fast |
| Two-way synchronisation | unison | rsync and scp are one-way |
Understand scp in 2 minutes
The model below sums up the command: an option set, a source, the encrypted SSH tunnel the data travels through, and a destination. Keep the order in mind, it is the same as cp.
🧠 Mental model: How scp works
scp = Source → [Encrypted SSH tunnel] → Destination
Key points
- scp copies files through an encrypted SSH tunnel
- The user@host:path syntax points to a remote location
- Without -r, scp only copies files (not directories)
- -P (uppercase) for the port, -p (lowercase) to preserve permissions
- No transfer resume: if the link drops, you start over
- For recurring transfers, prefer rsync over scp
Golden rules
Essential vocabulary
- -r
- Recursive: copies directories and their contents
- -P (uppercase)
- Set the SSH port (watch out: uppercase!)
- -p (lowercase)
- Preserve permissions and timestamps
- -C
- Enable compression during the transfer
- -q
- Quiet mode (no progress bar)
- -v
- Verbose mode (SSH connection details)
📚 Going further: 7 advanced options
- -l
- Limit the bandwidth (in Kbit/s)
- -3
- Copy between 2 servers through the local machine (already the default behaviour)
- -R
- Copy between 2 servers directly, the first one connecting to the second
- -i
- Point to a specific SSH private key file
- -o
- Pass SSH options (StrictHostKeyChecking, etc.)
- -F
- Use an alternative SSH config file
- -c
- Choose the encryption algorithm
The mechanism: scp opens an SSH session to the remote machine, then transfers the file inside that encrypted channel. Since OpenSSH 9.0, it no longer speaks the old SCP protocol but the SFTP protocol over the same connection; the command line has not changed, but the expansion of wildcards and of ~ is now handled differently. The -O option switches back to the old protocol for servers that do not implement SFTP.
Minimal syntax
scp takes the argument order of cp: the source first, the destination second. The only difference is that one of the two can carry a user@machine: prefix. That prefix, and it alone, triggers the SSH connection; without it, scp simply falls back to a local copy.
scp [OPTIONS] SOURCE DESTINATIONThe 3 usage scenarios
The direction of the transfer is not given by an option but by the position of the remote prefix. Spot where the : sits in the command and you know what leaves and what arrives. The third case is the only one involving two servers: the copy then travels through your workstation, which uses your bandwidth twice.
| Scenario | Syntax | Example |
|---|---|---|
| Local → Remote | scp file user@srv:/dest/ | scp config.conf admin@web-01:/etc/nginx/ |
| Remote → Local | scp user@srv:/file /local/ | scp admin@web-01:/var/log/app.log ~/logs/ |
| Remote → Remote | scp -3 user1@srv1:/f user2@srv2:/dest/ | scp -3 admin@web-01:/log admin@backup:/logs/ |
The -p vs -P trap
This is the number one source of errors with scp. The two options differ only by their case, and both exist: a mistake is therefore never rejected as an unknown option, it silently does something else. The block below shows the real message returned by OpenSSH 9.6, 9.9 and 10.0 when -p is typed instead of -P.
| Option | Meaning | Example |
|---|---|---|
-p (lowercase) | Preserves permissions and timestamps | scp -p script.sh user@srv:/opt/ |
-P (uppercase) | Custom SSH Port | scp -P 2222 file user@srv:/tmp/ |
Memory aid: Port = uppercase P, permissions = lowercase p.
# Common mistake: -p instead of -P for the portscp -p 2222 file.txt user@server:/tmp/# scp: stat local "2222": No such file or directory# -p enables preservation, "2222" becomes a source file to copy
# Correct: -P (uppercase) for the portscp -P 2222 file.txt user@server:/tmp/The essential options
Eight options cover almost every everyday transfer. The first five are the ones you will type weekly; -l and -i show up as soon as you work on a shared link or in a script. The "standard combo" block right after shows how they chain on a single line.
| Option | Meaning | Usage |
|---|---|---|
-r | Recursive | Copy a complete directory |
-P <port> | SSH port | SSH server on a non-standard port |
-p | Preserve | Keep permissions and timestamps |
-C | Compression | Speed up slow connections |
-v | Verbose | Debug SSH problems |
-q | Quiet | No progress bar |
-l <Kbit/s> | Limit | Control the bandwidth |
-i <key> | Identity | Use a specific SSH key |
The standard combo:
# Simple upload to a serverscp config.conf admin@web-01:/etc/nginx/
# Complete directory with compression and custom portscp -r -C -P 2222 ~/project/ deployer@prod:/var/www/app/
# Fetch logs while preserving timestampsscp -p admin@web-01:/var/log/app.log ~/logs/Transfer a complete directory
By default, scp only copies regular files. To copy a directory with all its content, the -r option (recursive) is mandatory. Without it, scp refuses the copy with two lines, measured on Debian 13 and AlmaLinux 10: the first gives the cause, the second the failed operation.
# Error: scp refuses to copy a directory without -rscp folder user@server:/tmp/# scp: local "folder" is not a regular file# scp: failed to upload file folder to /tmp/
# Correct: -r for recursivescp -r folder/ user@server:/tmp/Transfer between two remote servers
The -3 option copies a file from one server to another using your local machine as a relay. It is the answer when the two servers are not allowed to talk to each other directly, a common case in a segmented network.
# Copy logs from web-01 to backup-01, through your machinescp -3 admin@web-01:/var/log/app.log admin@backup-01:/logs/On the three reference distributions, this transit through the local machine is already the default behaviour: the manual page of OpenSSH 9.6 (Ubuntu 24.04), 9.9 (AlmaLinux 10) and 10.0 (Debian 13) states it plainly under -3, "This mode is the default". The -R option asks for the opposite, a direct copy where the first server connects to the second one itself, which requires it to be able to authenticate there. Writing -3 remains useful: the command says what it does, and it keeps the same meaning on an older machine where the direct copy was the rule.
Use a specific SSH key
For automated transfers (scripts, CI/CD), use a dedicated key: it avoids storing a password in clear text and can be revoked on its own without affecting your other accesses.
# Specific SSH keyscp -i ~/.ssh/deploy_key app.tar.gz deployer@prod:/releases/
# Combined with a custom portscp -i ~/.ssh/deploy_key -P 2222 app.tar.gz deployer@prod:/releases/Compression and bandwidth
On slow connections or for large text files, compression speeds up the transfer. The gain is marginal on data that is already compressed, such as an archive or an ISO image. The second command caps the rate so as not to saturate a shared link.
# Compression enabledscp -C database-dump.sql admin@backup:/dumps/
# Limit the bandwidth (in Kbit/s, not KB/s!)scp -l 800 big-file.iso user@server:/data/# 800 Kbit/s ≈ 100 KB/sUnit of -l: Kbit/s
The -l option uses Kbit/s (not KB/s). To get 100 KB/s, you must specify 800 (100 × 8). It is a frequent source of errors.
Security good practices
scp inherits the whole SSH configuration: transport encryption is therefore never the weak point. The real risks lie elsewhere, in the rights of the account used and in what the file becomes once dropped. The four reflexes below answer three questions: which identity do I connect with, where do I write, and how do I check afterwards. The third point matters most in production: copying straight into /etc/ as root gives an automated transfer write access to the whole system configuration.
- Use SSH keys rather than passwords for automation
- Check the permissions after the transfer:
ssh user@srv "ls -la /path/" - Prefer a non-root account for transfers, then move the file with
sudoif needed - Monitor connections:
sudo journalctl -u sshon Debian and Ubuntu,sudo journalctl -u sshdon RHEL and AlmaLinux
When to switch to rsync
scp is ideal for one-off transfers. As soon as a transfer repeats or becomes large, rsync does better, and the table shows on which criteria. The only line where scp keeps the advantage is availability: it ships with the SSH client, whereas rsync must be installed on both ends.
| Criterion | scp | rsync |
|---|---|---|
| Transfer resume | No, starts over | Yes, resumes where it stopped |
| Delta transfer | No, copies everything | Yes, only copies the changes |
| File exclusion | No | Yes, --exclude |
| Dry-run | No | Yes, -n to test |
| Simplicity | Simpler | More options to learn |
| Availability | Everywhere | Not always installed |
Simple rule: one-off transfer → scp. Recurring or large transfer → rsync.
Pitfalls to avoid
These mistakes come back constantly with scp. Knowing them avoids failed transfers and files overwritten by mistake. Each one is presented the same way: the command that goes wrong, the symptom, the cause, then the correct form.
Mixing up -p and -P
scp -p 2222 file.txt user@server:/tmp/Symptom: the port is ignored and scp tries to copy a file named 2222.
Cause: -p (lowercase) preserves permissions; -P (uppercase) changes the SSH port. Memory aid: Port = uppercase P.
scp -P 2222 file.txt user@server:/tmp/Forgetting -r for directories
scp folder/ user@server:/tmp/Symptom: not a regular file error, nothing is copied.
Cause: by default, scp only copies regular files, never directories.
scp -r folder/ user@server:/tmp/No transfer resume
scp big-file.iso user@server:/data/ # network cutSymptom: after a cut, the transfer starts over from zero.
Cause: scp does not support transfer resume, unlike rsync.
rsync -avP big-file.iso user@server:/data/Spaces in remote paths
scp user@server:/home/user/My Folder/file.txt .Symptom: error or file not found.
Cause: the shell treats the space as an argument separator. Without protection, scp receives two sources, …/My and Folder/file.txt.
scp "user@server:/home/user/My Folder/file.txt" .Wrapping the whole argument in quotes is enough with the SFTP protocol, used by default since OpenSSH 9.0. If you force the old protocol with -O, the path is also interpreted by a remote shell: you then need a double protection, "user@server:/home/user/My\ Folder/file.txt".
Silent overwrite without confirmation
scp user@server:/config/new.conf ~/config/important.confSymptom: the local file is overwritten without any warning.
Cause: scp overwrites the destination without asking for confirmation; there is no equivalent of cp -i.
cp ~/config/important.conf ~/config/important.conf.bak && scp user@server:/config/new.conf ~/config/important.confUnit of -l in Kbit/s, not KB/s
scp -l 100 file user@srv:/tmp/ # thinking it limits to 100 KB/sSymptom: transfer much slower than expected, around 12.5 KB/s.
Cause: -l is expressed in Kbit/s; 100 Kbit/s are only ~12.5 KB/s.
scp -l 800 file user@srv:/tmp/ # 800 Kbit/s ≈ 100 KB/sHands-on practice
This lab has you handle scp on realistic files, from a simple upload to bandwidth limiting. Allow 15 minutes.
Prepare the ground
The script below creates test files representative of server administration: a configuration, a deployment script, a log file and a small project tree.
# Create the labmkdir -p ~/scp-lab/{local,received}cd ~/scp-lab/local
# Typical configuration filecat > config.conf << 'EOF'# nginx configurationserver { listen 80; server_name example.com; root /var/www/html;}EOF
# Deployment scriptcat > deploy.sh << 'EOF'#!/bin/bashecho "Deployment in progress..."systemctl restart nginxecho "Deployment finished."EOFchmod +x deploy.sh
# Log filefor i in $(seq 1 50); do echo "$(date -d "$i hours ago" '+%Y-%m-%d %H:%M:%S') INFO Event $i" >> app.logdone
# Directory with sub-directoriesmkdir -p project/{src,docs,tests}echo "print('hello')" > project/src/main.pyecho "# Documentation" > project/docs/README.mdecho "def test(): pass" > project/tests/test_main.py
echo "Lab created in ~/scp-lab"ls -laR ~/scp-lab/localTo clean everything up at the end: rm -rf ~/scp-lab.
The 8 lab steps
These eight steps assume an accessible SSH server: a virtual machine, a test server, or your own workstation if sshd runs on it and your public key is in ~/.ssh/authorized_keys. Replace user@server everywhere with your real values. Work through them in order: steps 1 to 3 set the syntax, steps 4 to 8 add one option at a time so you can tell which one changes what. After each upload, check the result on the remote side with ssh user@server "ls -l /tmp/".
-
Copy a file to a server, send
config.confto the remote/tmp/:scp ~/scp-lab/local/config.conf user@server:/tmp/. Theuser@server:/pathsyntax designates the remote destination; everything goes through SSH. -
Fetch a file from a server, bring back
/etc/hostname:scp user@server:/etc/hostname ~/scp-lab/received/. Source and destination swap: the server becomes the source. -
Copy a complete directory, send
project/with-r:scp -r ~/scp-lab/local/project user@server:/tmp/. Without-r, scp refuses to copy a directory. -
Use a custom SSH port, for a server on port 2222:
scp -P 2222 ~/scp-lab/local/deploy.sh user@server:/tmp/. Careful, it is uppercase-P. -
Preserve permissions, copy
deploy.shkeeping its executable bit:scp -p ~/scp-lab/local/deploy.sh user@server:/tmp/. Lowercase-ppreserves permissions and timestamps. -
Transfer with compression, send
app.logcompressed:scp -C ~/scp-lab/local/app.log user@server:/tmp/.-Cspeeds up the transfer of text files. -
Verbose mode for debugging, watch the SSH negotiation:
scp -v ~/scp-lab/local/config.conf user@server:/tmp/.-vdisplays authentication, algorithms and detailed errors. -
Limit the bandwidth, cap the rate at 800 Kbit/s:
scp -l 800 ~/scp-lab/local/app.log user@server:/tmp/. That is about 100 KB/s.
Progressive exercises
These exercises go from the first transfer to the diagnosis of a failure. Read the statement, look for the command, then unfold the solution.
Foundations level
The first three exercises check the basic syntax: which argument comes first depending on the direction of the transfer, and when -r becomes mandatory.
Exercise 1, First transfer. Send the file config.conf to the server web-01 into /tmp/.
Hint: syntax scp file user@server:/path/.
See the solution
scp config.conf admin@web-01:/tmp/The basic scp syntax: local file first, remote destination second.
Exercise 2, Fetching logs. Fetch the file /var/log/syslog from the server web-01 into ~/logs/.
Hint: the remote source comes first.
See the solution
scp admin@web-01:/var/log/syslog ~/logs/To fetch a file, the remote source is the first argument, the local destination the second.
Exercise 3, Directory transfer. Send the whole ~/project/ directory to /var/www/ on the server prod.
Hint: -r is mandatory for directories.
See the solution
scp -r ~/project/ deployer@prod:/var/www/Without -r, scp refuses to copy a directory, it is the most frequent error.
Composition level
These two exercises bring in the options whose case matters: -P for the port, -p for permissions.
Exercise 4, Non-standard SSH port. Send backup.tar.gz to a server that uses SSH port 2222.
Hint: -P (uppercase) for the SSH port.
See the solution
scp -P 2222 backup.tar.gz admin@srv:/backups/Watch the case: uppercase -P = port, lowercase -p = permissions.
Exercise 5, Transfer with preservation. Send a script while preserving its execution permissions.
Hint: -p preserves permissions and timestamps.
See the solution
scp -p deploy.sh admin@prod:/opt/scripts/-p preserves modification times and permissions, including the executable bit.
Industrialisation level
Here the transfer involves two servers or combines several options on one line, as in a real deployment.
Exercise 6, Transfer between two servers. Copy /var/log/app.log from web-01 to backup-01 through your machine.
Hint: -3 uses your machine as a relay.
See the solution
scp -3 admin@web-01:/var/log/app.log admin@backup-01:/logs/-3 routes the data through the local machine, which is already the default behaviour on current OpenSSH versions: writing it makes the intent explicit. It is -R that would request a direct connection between the two servers.
Exercise 7, Combined options. Send a directory with compression, port 2222 and permission preservation.
Hint: scp options combine freely.
See the solution
scp -r -C -P 2222 -p ~/project/ admin@srv:/var/www/-r -C -P 2222 -p covers most needs of a somewhat demanding transfer.
Security level
The last exercise is about diagnosis: finding out why a transfer fails before touching anything.
Exercise 8, Debugging a failed transfer. Diagnose a failing transfer by displaying the SSH details.
Hint: -v displays the SSH connection details.
See the solution
scp -v config.conf admin@srv:/tmp/-v shows the SSH negotiation: authentication, algorithms and detailed errors.
Troubleshooting
When a transfer fails, these diagnosis methods and these frequent errors cover the vast majority of cases. Start with the method, which tells you where to look, then match the message against the table.
Diagnosis methods
When a transfer fails, the first question to settle is: does the problem come from SSH or from scp? The four commands below answer in that order. If ssh user@server "echo OK" works but scp fails, the connection and authentication are not at fault: look at the write permissions on the destination or at the SFTP subsystem on the server side. If ssh already fails, there is no point debugging scp. The -v output is verbose: the useful line is almost always the last one before the failure.
# Verbose mode: all SSH and transfer detailsscp -v file.txt user@server:/tmp/
# Test the SSH connection before scpssh -v user@server "echo OK"
# Check that the SSH port is opennc -zv server 22
# Test with a small file to rule out a size problemecho "test" | ssh user@server "cat > /tmp/test.txt"Frequent errors
These five messages cover almost every failure. Four of them actually come from SSH, not from scp: only not a regular file is specific to the copy. Take REMOTE HOST IDENTIFICATION HAS CHANGED! seriously: the message means that the host key presented no longer matches the one stored in ~/.ssh/known_hosts. The benign cause is a reinstalled server, but removing the entry without checking with the administrator amounts to accepting a possible interception.
| Error | Probable cause | Solution |
|---|---|---|
Permission denied | No write rights, or destination directory missing | Adjust permissions or copy to an allowed directory (/tmp/) |
connect to host ... port 22: Connection refused | sshd stopped, firewall, or different port | Start sshd, open the port, or use -P <port> |
REMOTE HOST IDENTIFICATION HAS CHANGED! | Reinstalled server, or MITM attack (rare) | Check with the admin, then ssh-keygen -R server and reconnect |
not a regular file | Copying a directory without the -r option | Add -r to copy recursively |
No space left on device | Destination disk full or quota reached | Check with ssh user@server "df -h", free some space |
scp cheat sheet
Keep it at hand: the general syntax and the options you use most every day.
General syntax
Three forms are enough for every case. What sets them apart is the position of the user@host: prefix: on the right to send, on the left to fetch, on both sides for a transfer between servers. The final dot in the second form designates the current directory, a handy shortcut when you bring back a log to analyse it locally.
| Syntax | Meaning | Example |
|---|---|---|
scp file user@host:/path/ | Send a file | scp app.conf admin@srv:/etc/ |
scp user@host:/path/file . | Fetch a file | scp admin@srv:/var/log/app.log . |
scp -3 user1@srv1:f user2@srv2:/ | Transfer between servers | scp -3 a@s1:/log b@s2:/log/ |
Common options
Options combine freely and their order does not matter, as long as each option expecting a value is followed by its own. Two warnings about the last two lines of the table. -o StrictHostKeyChecking=no disables the host key check: keep it for a disposable lab, never for a production script, where it cancels the protection against interception. As for -c, it is only useful to interoperate with an old server; the OpenSSH default choice is safer than most algorithms you might be tempted to force.
| Syntax | Meaning | Example |
|---|---|---|
-r | Recursive copy (directories) | scp -r folder/ user@srv:/tmp/ |
-P <port> | SSH port (uppercase) | scp -P 2222 f.txt user@srv:/ |
-p | Preserves permissions and timestamps | scp -p script.sh user@srv:/ |
-C | Enables compression | scp -C dump.sql user@srv:/ |
-v | Verbose mode (debug) | scp -v f.txt user@srv:/ |
-q | Quiet mode | scp -q f.txt user@srv:/ |
-l <Kbit/s> | Limits the bandwidth | scp -l 800 f user@srv:/ |
-i <key> | SSH private key | scp -i ~/.ssh/key f user@srv:/ |
-o <option> | One-off SSH option | scp -o StrictHostKeyChecking=no f srv:/ |
-c <algo> | Encryption algorithm | scp -c aes256-ctr f user@srv:/ |
Mastery checklist
You master scp when you can tick each of these points without hesitating. A point you cannot tick sends you back to the matching section above.
- I can send a file to a remote server.
- I can fetch a file from a remote server.
- I can copy a complete directory with
-r. - I can use a custom SSH port (
-P). - I can preserve permissions and timestamps with
-p. - I can transfer between two servers with
-3. - I know the difference between
-pand-P. - I know when to use rsync instead of scp.
- I can diagnose a connection problem with
-v.
Key points
-r: mandatory to copy a directory-P(uppercase) for the port,-p(lowercase) for permissions-3: relay through your machine, already the default on current OpenSSH-v: essential to diagnose SSH problems- No resume: if the transfer is interrupted, it starts over
- Prefer rsync for regular or large transfers
Knowledge check
Check that the essentials of this guide stuck. The questions cover only what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Transfer files with SFTP
A new full quiz with random questions
Work again only on the questions you missed