Terminal showing a real scp file transfer and sftp listing

SCP and SFTP on Ubuntu: Transfer Files Over SSH (with Examples)

If SSH gets you a remote shell, SCP and SFTP get you remote files. Both ride on the same SSH connection — same authentication, same port, same firewall config. If ssh user@host works, both file transfer methods will too.

This guide covers the practical syntax you’ll actually use, plus when rsync is a better fit than either.

Tested on: Ubuntu Server 22.04 LTS, 24.04 LTS, Windows 11. The commands are the same on all modern Unix-like systems, but what scp does underneath changed in OpenSSH 9.0 (see below): Ubuntu 22.04 ships OpenSSH 8.9, 24.04 ships 9.6 and 26.04 ships 10.2.

Terminal showing a real scp file transfer and sftp listing
Real command output from a test machine, not a mock-up.

SCP basics

SCP copies files between machines over SSH. The syntax mirrors cp, with user@host:path for the remote side.

Upload a file to the server

scp ./local-file.txt user@server:/home/user/

This copies local-file.txt from your current directory to /home/user/ on the server. If the destination is an existing directory, the file goes inside it with or without a trailing slash. If it does not exist, scp creates a file with that name instead, so a trailing slash on a directory you mean to copy into is a cheap safety habit.

Download a file from the server

scp user@server:/home/user/remote-file.txt ./

Just reverse the source/destination.

Copy an entire directory (recursive)

scp -r ./local-folder/ user@server:/home/user/

The -r flag means recursive. Without it, SCP only copies regular files, not directories.

Preserve permissions and timestamps

scp -p ./file.txt user@server:/home/user/

The -p (preserve) flag keeps the modification time, access time, and permissions on the copied file. Useful for backup scripts.

Use a specific SSH key

scp -i ~/.ssh/id_ed25519_homelab ./file.txt user@server:/home/user/

By default SCP uses your default key (~/.ssh/id_ed25519 or id_rsa). For multiple keys, use -i or set up ~/.ssh/config with per-host IdentityFile (see the SSH Key Authentication guide).

Use a non-default SSH port

scp -P 2222 ./file.txt user@server:/home/user/

Important: SCP uses capital -P for port. SSH itself uses lowercase -p. They don’t agree. If you typo, you’ll get a confusing error — check your case.

Copy between two remote servers (less common)

scp user1@host1:/path/file.txt user2@host2:/dest/

Works. Since OpenSSH 8.7 (so every supported Ubuntu release) the data is routed through your local machine by default; the -3 flag that used to request this is now simply the default behaviour. Use -R if you want host1 to connect to host2 and copy directly, which needs host1 to be able to authenticate to host2 without a password. For frequent server-to-server copies, you probably want rsync over SSH from one of the hosts directly.

scp changed in OpenSSH 9.0

From OpenSSH 9.0, scp uses the SFTP protocol by default instead of the legacy SCP/RCP protocol. That covers Ubuntu 24.04 (OpenSSH 9.6) and 26.04 (OpenSSH 10.2); Ubuntu 22.04 (8.9) still uses the legacy protocol by default. The command line is unchanged, but three things behave differently:

  • No more double quoting. The legacy protocol passed remote filenames through the remote shell, so names with spaces or shell characters needed extra quoting (scp 'host:"my file.txt"' .). In SFTP mode that quoting is no longer needed and can make the transfer fail. Quote the path once, as you would locally.
  • Servers without SFTP. If the remote end has no SFTP subsystem (some embedded devices, appliances, or an sshd_config with the Subsystem sftp line removed), a new client fails, typically with subsystem request failed on channel 0 and scp: Connection closed. Add -O to force the legacy protocol: scp -O file.txt user@device:/tmp/.
  • ~ paths and wildcards on older servers. Expanding a remote path such as host:~/file needs support on the server side; against old SFTP servers, and for some wildcard patterns, -O is the documented fallback.

SFTP basics

SFTP gives you an interactive prompt — like FTP, but over SSH. Useful for browsing remote directories or doing many file operations in one session.

Connect

sftp user@server

You get an sftp> prompt. Type help for the full command list. The most useful:

ls                  # list remote directory
cd /path            # change remote directory
lls                 # list LOCAL directory
lcd /path           # change LOCAL directory
get file.txt        # download
put file.txt        # upload
mget *.txt          # download multiple
mput *.txt          # upload multiple
mkdir foo           # create remote directory
rm file.txt         # delete remote file
quit                # exit

Recursive get / put

Same -r flag:

get -r /remote/folder
put -r /local/folder

When to use SCP vs SFTP

Need Tool
Quick one-off file copy SCP
Multiple files in one session, browsing as you go SFTP
Scripted file transfer with retry, partial transfer recovery rsync (next section)
GUI file transfer FileZilla, Cyberduck, WinSCP — all speak SFTP, so they work against any SSH server

For most one-off transfers, SCP. For exploring a remote filesystem, SFTP.

Why rsync is usually better for anything non-trivial

Both SCP and SFTP transfer the entire file every time. rsync only transfers what’s changed. This matters when:

  • Backing up a directory tree regularly (after the first run, only changed files are sent, so repeat copies are far faster)
  • Resuming a transfer that got interrupted
  • Mirroring large files where small changes happen

Basic rsync over SSH:

# Upload, recursively, preserving permissions, showing progress
rsync -av --progress ./local-folder/ user@server:/path/to/remote-folder/

# Download
rsync -av --progress user@server:/path/to/remote-folder/ ./local-folder/

Trailing slashes matter for rsync. ./local-folder/ (with slash) means “copy the contents of local-folder INTO remote-folder”. ./local-folder (no slash) means “copy local-folder AS A DIRECTORY into remote-folder”, creating remote-folder/local-folder/.

For backups: add --delete to mirror exactly, including removing files on the destination that no longer exist on the source. Be careful — --delete can wipe out files if you point it at the wrong directory.

For large file transfers over slow links: add -z for compression, --partial so interrupted transfers can resume.

rsync is the right tool for anything backup-shaped. SCP/SFTP are for one-off transfers.

Common errors

“Permission denied” during transfer

The SSH connection authenticates, but the destination directory isn’t writable by your user. Check ownership and permissions on the destination:

ls -ld /destination/path

If the directory is owned by another user, you need either sudo (which doesn’t work over SCP/SFTP directly — you’d need to copy to a writeable location first, then sudo mv on the server) or to fix ownership.

“scp: ambiguous target”

This comes from the legacy SCP protocol (the default on 22.04, or -O on newer releases): the remote shell split the destination path into more than one word, usually because it contains a space or shell character.

scp file.txt user@host:'/srv/my files/'        # fails in legacy mode: ambiguous target
scp -O file.txt user@host:'"/srv/my files/"'   # legacy mode: quote again for the remote shell
scp file.txt user@host:'/srv/my files/'        # fine on 24.04 and 26.04 (SFTP mode)

Separately, remember the trailing-slash rule from earlier: copying to /some/path creates a file called path if that directory doesn’t exist.

“sftp: command not found”

That message comes from your local shell: the sftp client isn’t installed on the machine you’re typing on. On Ubuntu or Debian, install it with sudo apt install openssh-client.

If the client runs but the connection fails with subsystem request failed on channel 0 and Connection closed, the problem is on the server: SFTP isn’t enabled. Check /etc/ssh/sshd_config (and files in /etc/ssh/sshd_config.d/) for:

Subsystem sftp /usr/lib/openssh/sftp-server

This line should be present and uncommented, and the openssh-sftp-server package installed. If you’ve manually edited sshd_config and removed it, SFTP won’t work, and on 24.04 and later plain scp stops working too unless you add -O.

Slow transfers on a fast network

Two common causes:

  1. CPU-bound encryption on slower hardware (Pi Zero, ancient laptops). Try a lighter cipher: scp -c [email protected] ...
  2. Compression hurting more than helping on already-compressed data. OpenSSH leaves compression off by default, so this only applies if you turned it on with -C or Compression yes in ~/.ssh/config; override it with -o "Compression=no".

Programmatic file transfer in scripts

For automation, prefer rsync over SCP — it handles edge cases better. If you must SCP in a script, use:

scp -B -i ~/.ssh/key file.txt user@host:/path/

-B is batch mode — fails fast instead of prompting for input. Combined with key auth, this works in unattended scripts.

For SFTP in a script:

sftp -b - user@host <<EOF
put file.txt
get remote-file.txt
quit
EOF

-b - reads commands from stdin (the heredoc). Useful for unattended SFTP.

What this doesn’t cover

  • Cloud storage sync (rclone, gdrive, S3 cli) — different tools for different use cases.
  • Continuous file sync (Syncthing, Resilio) — if you want bidirectional always-on sync between machines, neither SCP nor SFTP nor rsync is the right tool.
  • SFTP with restricted users (chroot jails, scponly shells) — you might want this for users you don’t want to give shell access to but DO want to give file transfer to. Search “openssh chroot sftp” for canonical guides.

From Config Files to CI/CD Pipelines

SCP and SFTP are how you move config files around a homelab. They are also how CI/CD pipelines push build artifacts to staging servers, how Ansible copies playbooks to remote hosts, and how backup scripts pull database dumps offsite. Same protocol, same commands, different context.

If you can rsync your Jellyfin library to a backup drive over SSH, you can write the deployment step in a GitHub Actions workflow that pushes a release to a production server. The syntax is the same. The confidence to automate file transfers over SSH comes from doing it manually first, which is exactly what you are learning here.


Companion guides: Enable SSH on Ubuntu Server, SSH Key Authentication on Ubuntu, SSH Hardening, Common SSH Errors.

Enjoyed this guide?

New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.

Scroll to Top