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, macOS 14, Windows 11. SCP and SFTP work identically across all modern Unix-like systems.

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. The trailing slash on the destination matters — without it, the file is copied AS /home/user, which is confusing.

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, but the data routes through your local machine unless you use -3 (which routes through both remote hosts directly). For frequent server-to-server copies, you probably want rsync over SSH from one of the hosts directly.

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 use SFTP under the hood

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 (incremental copies are 100x faster after the first run)
  • 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”

Usually a typo in the destination. SCP can’t tell whether you want to copy to a file or a directory.

scp file.txt user@host:/some/path     # creates /some/path AS the file
scp file.txt user@host:/some/path/    # places file.txt INTO /some/path/

The trailing slash distinguishes them.

“sftp: command not found”

The sftp server isn’t enabled on the remote. Check /etc/ssh/sshd_config for:

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

This line should be present and uncommented. If you’ve manually edited sshd_config and removed it, SFTP won’t work.

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. Disable 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