Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Troubleshooting

scp Port: How to Copy Files Over a Non-Standard SSH Port

Sean

Platform Writer

Aug 20, 2026
8 min read

scp takes the port with a capital -P, not the lowercase -p that ssh uses. That single letter is the reason most scp-over-a-custom-port commands fail on the first try.

scp Port: How to Copy Files Over a Non-Standard SSH Port

It is one of the most reliably annoying inconsistencies in the OpenSSH toolchain. ssh -p 2222 host works. scp -p 2222 host:/file . does not — because in scp, lowercase -p means preserve modification times and permissions, and it does not take an argument. So scp reads 2222 as your source path, fails to find it, and gives you an error that has nothing to do with ports.

The fix is one keystroke. The interesting part is everything around it: how to stop typing the port at all, what to do when the copy still hangs, and why scp behaves differently again if your OpenSSH is new enough to use SFTP underneath.

Table of contents

Why scp uses -P and ssh uses -p

The flags collide for historical reasons. scp was written as a wrapper around rcp, and rcp already used -p to mean preserve — keep the original file’s modification time, access time, and permission bits on the copy. When scp gained the ability to talk to a non-default port, -p was taken, so the port went to -P.

ssh had no such inheritance. It got the obvious lowercase -p for port, and the two tools have disagreed ever since. sftp sides with scp and also uses -P. rsync sidesteps the argument entirely by not having a port flag at all — you pass the port through to ssh with -e.

So the working form is:

# Copy a local file to a remote host listening on 2222
scp -P 2222 ./report.tar.gz [email protected]:/srv/backups/

# Copy a remote file down to the current directory
scp -P 2222 [email protected]:/var/log/app.log .

And the form that produces a confusing error:

$ scp -p 2222 [email protected]:/var/log/app.log .
2222: No such file or directory

The error names your port number as a missing file, which is exactly as helpful as it sounds. If you ever see a port number quoted back at you as a path, you typed the lowercase flag.

Stop typing the port: use ~/.ssh/config

Passing -P on every invocation is a habit worth not forming. If a host runs SSH somewhere other than 22, write it down once in ~/.ssh/config and every tool in the family — ssh, scp, sftp, rsync, git over ssh — picks it up automatically.

# ~/.ssh/config
Host prod
    HostName 203.0.113.10
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

Host staging
    HostName 203.0.113.44
    User deploy
    Port 2200

After that, the port disappears from your commands entirely:

scp ./report.tar.gz prod:/srv/backups/
ssh prod
rsync -av ./dist/ prod:/var/www/site/

This also removes an entire class of mistake: copying to staging when you meant production because you fat-fingered a port digit. A named host is harder to get wrong than a four-digit number, and it is self-documenting when someone else reads your deploy script six months later.

If you cannot edit the config — a CI runner, a container, someone else’s laptop — you can still set the port for a single command without the flag:

scp -o Port=2222 ./file.txt [email protected]:/tmp/

Non-standard ports do not make SSH more secure

Moving SSH off 22 is usually sold as a security measure. It is closer to a noise-reduction measure. Automated scanners hammer port 22 constantly, so relocating the daemon does cut your auth log down to a readable size. What it does not do is stop anyone who is actually looking at you — a full port scan finds the new port in seconds, and the SSH banner announces itself on whatever port it lands on.

The controls that genuinely matter are the boring ones: key-only authentication with PasswordAuthentication no, a firewall that only admits the addresses you expect, and PermitRootLogin no. A non-standard port on top of those is a small convenience. A non-standard port instead of those is a costume.

There is also a real operational cost. Every new engineer, every deploy script, every monitoring check and every backup job now needs to know about the port, and the ones that do not know fail with a timeout rather than a clear message. That is a permanent tax you pay for a temporary drop in log volume. Decide deliberately, and if you take it, put the port in an SSH config that ships with the repo.

When the port is right and the copy still fails

A correct -P does not guarantee a successful copy. The failures cluster into a few recognisable shapes, and the shape tells you where to look.

  • It hangs, then times out. Nothing is listening on that port, or a firewall is dropping packets silently. Test the path independently before blaming scp: nc -vz example.com 2222 or ssh -v -p 2222 example.com. A dropped connection times out; a closed port refuses immediately. The difference tells you firewall versus daemon.
  • Permission denied (publickey). The port is fine and the transport is fine — you failed authentication. Add -v and read which key was offered. Common cause: scp on a CI runner where the agent is not forwarded.
  • Permission denied on the destination path. You authenticated, but the remote user cannot write there. ssh -p 2222 host 'ls -ld /srv/backups' settles it in one command.
  • Not a regular file. You are copying a directory without -r. scp will not recurse unless told.
  • scp: command not found on the remote. Newer OpenSSH releases ship scp as an SFTP client by default, but some hardened images drop the server-side binary entirely. Use sftp -P or rsync -e 'ssh -p 2222' instead.

The -v flag is worth more than any of the guesswork above. It prints the ssh handshake underneath the copy, which is where nearly every scp problem actually lives.

scp is a fine tool with a shrinking remit

OpenSSH 9.0 changed scp’s default backend from the legacy remote-copy protocol to SFTP. The command-line interface stayed the same, which is why most people never noticed, but the old protocol had genuine problems around filename handling and it is on the way out. If you maintain scripts that depend on scp’s oldest quirks — wildcards expanded by the remote shell, for instance — those are the ones that break on upgrade.

For one file, moved once, scp is still the shortest thing to type. For anything repeated, rsync is the better tool: it skips unchanged files, resumes interrupted transfers, and reports what it did.

# rsync over a non-standard port
rsync -avz -e 'ssh -p 2222' ./dist/ [email protected]:/var/www/site/

# Dry run first, always
rsync -avzn -e 'ssh -p 2222' ./dist/ [email protected]:/var/www/site/

And for anything that runs on a schedule, neither is really the answer. A copy job that a person has to remember to run is a copy job that stops happening the week everyone is busy.

How this fits the rest of the stack

Copying build artefacts over SSH is a symptom of a deploy path that has not been automated yet. It works, it is scriptable, and it quietly becomes the thing only one person on the team knows how to do. The alternative is to make the deploy the artefact: push to a repository, let the platform build it, and read the build log when it fails.

That is the shape RunxBuild deploys in — a service builds from your GitHub repo, gets a live route and runtime logs, and rolls back to the previous deploy when a release goes wrong. No port flags, no forgotten SSH config on the CI runner. If you are costing out what that looks like against a box you SSH into, the RunxBuild hosting calculator puts the service, the database, the storage and the bandwidth on one page as separate line items.

Useful related references:

FAQ

Why does scp use -P instead of -p for the port?

Because -p was already taken. scp inherited it from rcp, where lowercase -p means preserve the file’s modification time and permissions. When port selection was added, the capital -P was the next available letter. sftp follows the same convention; ssh uses lowercase -p because it had no legacy flag to work around.

What error do I get if I use lowercase -p by mistake?

scp treats the port number as a file path and reports something like 2222: No such file or directory. The error never mentions ports, which is why the mistake is so easy to stare past. If you see a bare port number quoted back at you as a missing file, switch to capital -P.

How do I set the SSH port permanently so I never pass it again?

Add a Host block to ~/.ssh/config with a Port line. Every tool in the OpenSSH family reads it — ssh, scp, sftp, and rsync over ssh — so scp ./file prod:/srv/ works with no flags at all. It also removes the risk of typing the wrong port and hitting the wrong environment.

Does running SSH on a non-standard port improve security?

Only marginally. It reduces automated scanner noise in your auth log, which is a real convenience, but a port scan finds the new port in seconds. Key-only authentication, a restrictive firewall, and disabling root login are the controls that matter. Treat a custom port as noise reduction, not as a defence.

Should I use scp or rsync for a non-standard port?

For a single file copied once, scp with -P is the shortest command. For anything repeated, use rsync with -e 'ssh -p 2222' — it skips unchanged files, resumes interrupted transfers, and tells you what it actually did. Run it with -n first to preview.

#scp port#scp#ssh#linux#file transfer