- The pattern is always
scp [options] source destination, where a remote path isuser@host:/path. - Copying a directory needs
-r. Without it scp silently skips the folder. -Psets the port, uppercase. Lowercase-ppreserves timestamps, which is a different flag entirely.- For anything over a few hundred megabytes, or anything you might have to run twice, use rsync instead: it resumes and skips unchanged files.
- Wrap remote paths containing spaces or wildcards in quotes, because they are expanded on the remote shell.
What is the basic scp command syntax?
scp copies files between machines over SSH, using the same keys and configuration as your normal SSH connection. The syntax is source first, destination second, exactly like cp. A remote location is written user@host:/path, and whichever side has the colon is the remote one.
# Local file to remote server
scp report.pdf deploy@203.0.113.10:/home/deploy/
# Remote file to local machine
scp deploy@203.0.113.10:/var/log/nginx/error.log ./
# Whole directory, both ways (note -r)
scp -r ./site-files deploy@203.0.113.10:/var/www/
scp -r deploy@203.0.113.10:/var/www/example.com ./backup/
# Between two remote servers, routed through your machine
scp -r deploy@server-a:/var/www/site deploy@server-b:/var/www/Without -r, directories are skipped
Older versions of scp print "not a regular file" and carry on; newer ones fail. Either way the folder does not arrive. If a copy appeared to succeed but nothing is there, the missing -r is the first thing to check.
Which scp options actually matter?
| Flag | Does | Use it when |
|---|---|---|
| -r | Copy directories recursively | Whenever the source is a folder |
| -P 2222 | Connect to a non-default SSH port | Your server does not use port 22 |
| -p | Preserve modification times and permissions | The timestamps matter, such as a backup |
| -C | Compress in transit | Text, code or SQL over a slow link |
| -i ~/.ssh/key | Use a specific private key | You have more than one key |
| -l 8000 | Limit to 8000 Kbit/s | Copying on a live server during working hours |
| -q | Quiet: no progress meter | Running inside a script or cron job |
| -v | Verbose SSH debugging | The connection fails and you need to know why |
Uppercase -P is the port. Lowercase -p preserves timestamps. Mixing them up is the single most common scp mistake.
# Non-standard port, specific key, preserve timestamps
scp -P 2222 -i ~/.ssh/deploy_key -p backup.tar.gz deploy@203.0.113.10:/var/backups/
# Compress a SQL dump in transit (large win on text)
scp -C database.sql deploy@203.0.113.10:/tmp/
# Throttle to roughly 1 MB/s so a live site keeps its bandwidth
scp -l 8000 -r ./uploads deploy@203.0.113.10:/var/www/example.com/wp-content/
# Diagnose a failing connection
scp -v file.txt deploy@203.0.113.10:/tmp/Compression helps text and hurts everything else. SQL dumps, logs and source code compress well. JPEGs, PNGs, MP4s and already-gzipped archives are incompressible, and -C just adds CPU on both ends for no gain.
How do I copy multiple files or use wildcards?
Local wildcards are expanded by your own shell before scp ever runs. Remote wildcards are expanded by the shell on the far side, which means they must survive your local shell untouched. That is why remote patterns need quoting.
# Several named local files to one destination
scp report.pdf invoice.pdf notes.txt deploy@203.0.113.10:/home/deploy/docs/
# Local wildcard: expanded by YOUR shell, no quotes needed
scp ./logs/*.log deploy@203.0.113.10:/tmp/logs/
# Remote wildcard: MUST be quoted, or your local shell eats it
scp 'deploy@203.0.113.10:/var/log/nginx/*.log' ./logs/
# Brace expansion works remotely too, inside quotes
scp 'deploy@203.0.113.10:/etc/nginx/{nginx.conf,mime.types}' ./config/
# Paths with spaces need escaping twice: once locally, once remotely
scp 'deploy@203.0.113.10:/home/deploy/My\ Documents/file.pdf' ./There is no --exclude in scp
scp cannot skip files. If you need to copy a site while leaving out node_modules, cache directories or logs, that is rsync's job, or tar the directory with --exclude first and copy the archive. Creating tar archives covers the exclude syntax.
When should I use rsync instead of scp?
Use scp for a one-off copy of a handful of files. Use rsync for anything else. rsync transfers only what has changed, resumes an interrupted transfer, and can exclude paths, which makes it the correct tool for site migrations and repeat syncs. It uses the same SSH connection, so nothing else about your setup changes.
| scp | rsync | |
|---|---|---|
| Resumes after a drop | No, starts from zero | Yes, with --partial |
| Skips unchanged files | No, copies everything | Yes, that is the point |
| Exclude patterns | No | Yes, --exclude |
| Progress detail | One overall bar | Per file plus a total |
| Delete removed files | No | Yes, with --delete |
| Installed by default | Yes, everywhere | Usually, but not guaranteed |
The first two rows are the ones that matter on a large transfer that fails at 90 percent.
# Standard: archive mode, compress, human-readable progress
rsync -avzh --progress ./site/ deploy@203.0.113.10:/var/www/example.com/
# Resume a large transfer that was interrupted
rsync -avzh --partial --progress big.tar.gz deploy@203.0.113.10:/var/backups/
# Skip what should never be copied
rsync -avzh --progress \
--exclude='node_modules' \
--exclude='wp-content/cache' \
--exclude='*.log' \
./site/ deploy@203.0.113.10:/var/www/example.com/
# Non-standard SSH port
rsync -avzh -e 'ssh -p 2222' ./site/ deploy@203.0.113.10:/var/www/
# See exactly what WOULD happen, change nothing
rsync -avzh --dry-run --delete ./site/ deploy@203.0.113.10:/var/www/example.com/./site/ copies the contents, ./site copies the folder itself into the destination.Always dry-run --delete first
--delete removes anything at the destination that is not at the source. Combined with a mistyped source path, or a missing trailing slash, it will empty the target directory. Run it with --dry-run first, every single time, and read the output.
How do I fix common scp errors?
| Error | Cause | Fix |
|---|---|---|
| Permission denied (publickey) | SSH key not accepted | Check with ssh -v user@host; scp uses the same auth |
| No such file or directory | The destination directory does not exist | scp does not create paths: mkdir -p on the remote first |
| not a regular file | Source is a directory and -r is missing | Add -r |
| Permission denied (after connecting) | The remote user cannot write there | Copy to /tmp, then move it with sudo |
| Connection refused | Wrong port, or sshd is not running | Add -P with the correct port |
| scp: command not found on remote | Newer OpenSSH defaults to the SFTP protocol | Add -O to force the legacy transfer mode |
The last row is new: OpenSSH 9 changed scp's default to SFTP under the hood, which breaks against some older or restricted servers.
# This fails: deploy cannot write directly into /var/www
scp site.tar.gz deploy@203.0.113.10:/var/www/
# Do it in two steps instead
scp site.tar.gz deploy@203.0.113.10:/tmp/
ssh deploy@203.0.113.10 'sudo mv /tmp/site.tar.gz /var/www/ && sudo chown deploy:www-data /var/www/site.tar.gz'Ownership does not survive the copy
Files arrive owned by the user you connected as, not by whoever owned them at the source. After copying site files you will usually need to reset ownership and permissions. Linux file permissions for web servers has the exact commands.
If you are moving a whole WordPress site rather than a few files, scp and rsync are only part of the job: the database, the URLs inside it and the DNS cutover all matter as much as the files. Our WordPress migration service does the whole sequence, including the database rewrite, at no cost.
Frequently asked questions
How do I copy a folder with scp?
Add -r: scp -r ./localfolder user@host:/remote/path/. Without it, scp skips the directory and the copy appears to succeed while nothing arrives. The destination path must already exist, because scp does not create directories.
What is the difference between -p and -P in scp?
Uppercase -P sets the SSH port, for example -P 2222. Lowercase -p preserves modification times and permission bits. They are unrelated, and confusing them produces a connection error rather than the transfer you wanted.
Is scp faster than rsync?
For a first copy of a single file they are near identical, since both use SSH. rsync wins on everything else: it skips unchanged files on a repeat run, resumes an interrupted transfer instead of restarting, and can exclude paths. For a large or repeated copy, rsync is faster in practice.
How do I scp a file to a non-standard SSH port?
Use uppercase -P: scp -P 2222 file.txt user@host:/path/. Note that rsync uses different syntax for the same thing: rsync -e 'ssh -p 2222'. This inconsistency catches people out regularly.
Why does scp say permission denied when I can SSH in fine?
Almost always because the remote user cannot write to the destination directory, rather than an authentication problem. Copy to /tmp first, then move the file with sudo over SSH. Do not enable root login to work around it.
Is scp deprecated?
The underlying protocol is considered outdated and OpenSSH 9 switched scp to use SFTP internally by default. The scp command itself still ships everywhere and works fine. For scripted or large transfers, rsync or sftp are the better long-term choices.
About G7Cloud Engineering
Articles written by the engineers who build and run G7Cloud: UK managed hosting and the AI Website Builder. We write about what we operate every day: containers, backups, databases, and the small-business websites that run on them.
More about G7Cloud →