Linux7 min readPublished: 15 Apr 2024Updated: 29 Jul 2026

SCP Command Examples: Copy Files Over SSH

Practical scp examples for copying files and folders between Linux servers over SSH, with the flags that matter and when rsync is the better tool for the job.

G7Cloud Engineering
Platform team
Share:
Abstract illustration representing scp command examples: copy files over ssh.
  • The pattern is always scp [options] source destination, where a remote path is user@host:/path.
  • Copying a directory needs -r. Without it scp silently skips the folder.
  • -P sets the port, uppercase. Lowercase -p preserves 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.

The four directions
# 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/
The last one is convenient but slow: the data travels to your machine and out again. Add -3 to make that explicit, or run it from one of the servers instead.

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?

FlagDoesUse it when
-rCopy directories recursivelyWhenever the source is a folder
-P 2222Connect to a non-default SSH portYour server does not use port 22
-pPreserve modification times and permissionsThe timestamps matter, such as a backup
-CCompress in transitText, code or SQL over a slow link
-i ~/.ssh/keyUse a specific private keyYou have more than one key
-l 8000Limit to 8000 Kbit/sCopying on a live server during working hours
-qQuiet: no progress meterRunning inside a script or cron job
-vVerbose SSH debuggingThe 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.

Options in practice
# 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/
-l is in kilobits per second, so 8000 is about 1 MB/s. Worth using on a production server in the middle of the day.
Pro Tip

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.

Multiple files, both directions
# 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' ./
The double escaping on the last line looks odd but is correct: the remote shell needs its own backslash after your quotes are stripped.

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.

scprsync
Resumes after a dropNo, starts from zeroYes, with --partial
Skips unchanged filesNo, copies everythingYes, that is the point
Exclude patternsNoYes, --exclude
Progress detailOne overall barPer file plus a total
Delete removed filesNoYes, with --delete
Installed by defaultYes, everywhereUsually, but not guaranteed

The first two rows are the ones that matter on a large transfer that fails at 90 percent.

The rsync commands worth memorising
# 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/
The trailing slash on the source is load-bearing: ./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?

ErrorCauseFix
Permission denied (publickey)SSH key not acceptedCheck with ssh -v user@host; scp uses the same auth
No such file or directoryThe destination directory does not existscp does not create paths: mkdir -p on the remote first
not a regular fileSource is a directory and -r is missingAdd -r
Permission denied (after connecting)The remote user cannot write thereCopy to /tmp, then move it with sudo
Connection refusedWrong port, or sshd is not runningAdd -P with the correct port
scp: command not found on remoteNewer OpenSSH defaults to the SFTP protocolAdd -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.

Copying to a directory you do not own
# 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'
This is the right pattern. Enabling root SSH login so scp can write anywhere trades a small inconvenience for a large security problem.

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.

Put this into practice on G7Cloud

Every site runs in its own dedicated container behind ScaleShield, with daily backups that are restore-tested every night. Start on the free plan, no card needed.

About G7Cloud Engineering

Platform team

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 →