scp file.txt user@host:/remote/path copies a file to a server over SSH, and scp user@host:/remote/file.txt . copies one back. Add -r for directories. It uses your existing SSH config and keys, so if ssh host works, scp works - which is why it is the first thing everyone reaches for. What the top results do not mention is that the OpenSSH project has formally deprecated the scp protocol, calling it outdated and inflexible, and modern scp quietly uses SFTP underneath. It still works and it is fine for one file, once. For anything you will do twice, rsync is simply better and it is worth switching now.
This is a tool most people learn once and use forever without noticing that the ecosystem moved on around it. It has not stopped working - but the reasons to reach for it have narrowed to one.
Table of contents
- The syntax
- The trailing-slash and quoting traps
- It is deprecated, and what that means
- rsync is the better tool for almost everything
- When scp is still the right call
- How this fits the rest of the stack
- FAQ
The syntax
# Local to remote.
scp file.txt user@host:/remote/path/
# Remote to local.
scp user@host:/remote/file.txt .
# A whole directory.
scp -r ./dist user@host:/var/www/
# Non-standard port. Capital P - lowercase p means preserve times.
scp -P 2222 file.txt user@host:/tmp/
# Between two remote hosts.
scp user1@host1:/file user2@host2:/path/
# Preserve timestamps and modes, and show progress.
scp -pv file.txt user@host:/tmp/
The flag that catches everyone: -P is the port, -p is preserve. This is the opposite of ssh, which uses lowercase -p for the port. Using -p for a port with scp silently does the wrong thing - it tries to preserve timestamps and then fails to connect on port 22.
Because scp runs over SSH, everything in ~/.ssh/config applies. If you have a Host prod entry, scp file.txt prod:/tmp/ works with no extra flags. Set that up once and most of scp’s awkwardness disappears.
The trailing-slash and quoting traps
Two behaviours that produce confusing results.
The destination directory must exist. scp does not create intermediate directories. Copying to /var/www/newdir/ when newdir does not exist gives you a file named newdir, not a directory containing your file. This is the single most common scp surprise.
# If /var/www/newdir does not exist, this creates a FILE called newdir.
scp file.txt user@host:/var/www/newdir/
# Make it first.
ssh user@host 'mkdir -p /var/www/newdir'
scp file.txt user@host:/var/www/newdir/
The remote path is expanded by the remote shell. Spaces and globs need quoting twice - once for your shell, once for theirs.
# Fails: the remote shell splits on the space.
scp "user@host:/path/my file.txt" .
# Works: escaped for the remote shell too.
scp "user@host:/path/my\ file.txt" .
This double-expansion is genuinely part of why OpenSSH deprecated the protocol: it is a design where filenames get interpreted by a remote shell, which is both surprising and a security consideration.
It is deprecated, and what that means
OpenSSH has described the scp protocol as outdated, inflexible, and not readily fixed. Since OpenSSH 9.0, scp uses SFTP as its default backend rather than the legacy protocol.
In practice:
- The command still works. It is not going away soon and your scripts will not break tomorrow.
- The old protocol had real problems - notably CVE-2020-15778 around remote command injection through filenames, which follows directly from the remote-shell expansion above.
- Behaviour can differ subtly between the legacy and SFTP backends in edge cases involving globs and unusual paths.
- No new features are coming. It is maintained, not developed.
So this is not an alarm. It is a signal about where to invest your habits. If you are writing a deploy script today, do not build it on scp.
rsync is the better tool for almost everything
rsync also runs over SSH, so the setup cost is zero, and it is better in every dimension that matters for repeated work.
# The everyday invocation.
rsync -avz ./dist/ user@host:/var/www/
# Preview before doing anything. scp has no equivalent.
rsync -avzn ./dist/ user@host:/var/www/
# Mirror exactly, deleting what is gone. Dry-run this first, always.
rsync -avz --delete ./dist/ user@host:/var/www/
# Skip things.
rsync -avz --exclude '.git' --exclude 'node_modules' ./ user@host:/srv/app/
# Non-standard port.
rsync -avz -e 'ssh -p 2222' ./dist/ user@host:/var/www/
What you get over scp:
- Only transfers differences. Re-running a 500MB deploy after a one-file change moves that one file. scp copies all 500MB every time.
- Resumes with
--partial. scp starts over. --dry-runshows exactly what would happen. This alone justifies the switch.--deletemirrors properly, so removed files are removed remotely.--excludekeeps.gitandnode_modulesout without a tarball dance.- Progress and stats that are actually informative.
The famous rsync trap, since we are here: the trailing slash on the source matters. rsync -a ./dist/ dest/ copies the contents of dist into dest. rsync -a ./dist dest/ copies the directory dist into dest, giving you dest/dist/. One character, entirely different result. Always dry-run first.
When scp is still the right call
It is not obsolete, it is narrow:
- One file, one time, interactively.
scp file.txt prod:/tmp/is shorter than the rsync equivalent and there is nothing wrong with it. - rsync is not installed on the remote and you cannot install it. rsync must exist on both ends; scp only needs SSH.
- Muscle memory in a hurry. A perfectly good reason at 2am.
For anything scripted, repeated, large, or resumable - a deploy, a backup, a sync - use rsync. And for interactive exploration of a remote filesystem, sftp gives you a session where you can look around before transferring, which scp cannot do.
The broader point: if you are copying build artifacts to a server by hand often enough to have opinions about scp versus rsync, the real answer is probably neither. A deploy that runs from a pipeline on a push is reproducible in a way that a person running a copy command never is - the file transfer is not the interesting part of the problem.
How this fits the rest of the stack
Hand-copying files to servers is one of those habits that works fine until the day the copy is half-finished, or lands on the wrong host, or nobody remembers which version is actually live. A deploy triggered by a push does not have those failure modes. If you are weighing what that setup costs, the RunxBuild hosting calculator shows the service, database, storage, and bandwidth as separate line items, and the RunxBuild dashboard is where the team sees which build is actually running.
Useful related references:
- How to Rename a File in Linux: mv, rename, and git mv
- hostnamectl set-hostname: How to Set the Hostname in Linux
- Change the Hostname on a Linux Machine Permanently
- Services on RunxBuild
FAQ
Is scp deprecated?
The scp protocol is, yes. OpenSSH describes it as outdated and inflexible, and since OpenSSH 9.0 the scp command uses SFTP as its backend instead. The command still works and is not disappearing soon, but it receives no new features. For anything scripted or repeated, use rsync or sftp; scp remains reasonable for a single interactive file copy.
What is the difference between scp and rsync?
scp copies everything every time. rsync transfers only differences, resumes interrupted transfers, offers --dry-run to preview, --delete to mirror, and --exclude to skip paths. Both run over SSH with the same keys and config. For a one-off single file, scp is shorter. For a deploy, a backup, or anything repeated, rsync is better in every respect.
How do I copy a directory with scp?
Use -r: scp -r ./localdir user@host:/remote/path/. Note that the destination’s parent directory must already exist - scp does not create intermediate directories, and copying to a non-existent path produces a file with that name rather than a directory. Create it first with ssh user@host 'mkdir -p /remote/path', or use rsync, which handles this correctly.
Why does scp use -P for the port instead of -p?
Because -p was already taken for preserving modification times and modes, matching cp. So scp uses capital -P for the port while ssh uses lowercase -p - an inconsistency that catches nearly everyone. Using lowercase -p with scp silently enables preserve mode and then tries to connect on the default port, which produces a confusing failure.
Can I use scp with an SSH config file?
Yes. scp runs over SSH and honours ~/.ssh/config completely, including Host aliases, User, Port, and IdentityFile. If you have a Host prod entry, scp file.txt prod:/tmp/ works with no extra flags. Setting up the config once removes most of scp’s flag awkwardness, and the same aliases work for ssh, rsync, and sftp.