RDP Monster

scp Command Linux: Copy Files to and from a VPS (rsync, sftp)

scp Command Linux: Copy Files to and from a VPS (rsync, sftp)

The scp command in Linux: syntax and the basic transfers

scp (secure copy) copies files between machines over SSH, so the user, key and port that work for ssh work for scp. The shape is always scp [options] SOURCE DESTINATION, and whichever side is remote is written user@host:path. It needs a working SSH login; if port 22 is not answering yet, enable SSH on Ubuntu and harden it first.

1. Local to remote (upload)

scp backup.tar.gz user@SERVER_IP:/home/user/
scp backup.tar.gz user@SERVER_IP:~/backups/renamed.tar.gz
scp *.conf user@SERVER_IP:~/nginx-conf/

A path ending in a slash drops the file inside that directory under its original name; a full path renames it on arrival. Several local files can precede the destination as long as the destination is a directory, and that directory must already exist: scp does not create parents.

2. Remote to local (download)

scp user@SERVER_IP:/var/log/nginx/access.log .
scp user@SERVER_IP:'/var/log/nginx/*.log' ./logs/

The dot means "here, same name". Quote remote wildcards so your local shell does not expand them. Remote-to-remote works too: scp -3 user@HOST_A:/data/dump.sql user@HOST_B:/data/ routes the copy through your machine, so only your machine needs credentials for both servers.

3. Directories with -r

scp -r ./site user@SERVER_IP:/var/www/
scp -r user@SERVER_IP:/etc/nginx ./nginx-backup

-r copies a directory and everything under it. The first command creates /var/www/site on the server; the second brings the whole nginx tree down into a local folder. Symlinks are copied as regular files, one reason rsync is the better tool for full system trees.

scp options you will actually use: -P, -i, -C, -p

scp -P 2222 file.txt user@SERVER_IP:/home/user/        # non-default SSH port
scp -i ~/.ssh/id_ed25519 file.txt user@SERVER_IP:~/     # use a specific key
scp -C dump.sql user@SERVER_IP:~/                       # compress in transit
scp -rp ./site user@SERVER_IP:/var/www/                 # keep times and modes

The port flag is a capital -P. Lowercase -p means "preserve modification times, access times and mode bits", and because ssh itself uses -p for the port, mixing them up is the most common scp mistake. To stop repeating the port and key on every call, define the host once in ~/.ssh/config; scp, rsync and sftp all read it:

Host vps
    HostName SERVER_IP
    User user
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

Two more flags: -l 8000 caps the transfer at 8,000 Kbit/s (about 1 MB/s), and -v shows which keys are offered when authentication fails. Since OpenSSH 9.0, scp speaks the SFTP protocol underneath; the syntax is unchanged, but shell tricks embedded in a remote path (command substitution, for example) no longer run on the server, and -O restores the legacy protocol for a very old server that rejects the new mode.

rsync: mirrors, resumable transfers and excludes

rsync copies only what changed. The first run behaves like scp -r; later runs compare both sides and send just the differences: new files, and for changed files only the modified blocks. That makes it the tool for deployments, backups and anything large. It must be installed on both ends (sudo apt install rsync); some minimal images and containers ship without it.

1. The everyday command

rsync -avz --progress ./site/ user@SERVER_IP:/var/www/site/
rsync -avz --progress user@SERVER_IP:/var/www/site/ ./site-backup/

-a (archive) expands to -rlptgoD: recurse, keep symlinks as symlinks, preserve permissions, times, group, owner and device files; ownership is only preserved when the receiving side runs as root. -v lists files, -z compresses, --progress shows each file.

The trailing slash matters. ./site/ means "the contents of site"; ./site means "the directory itself", which would create /var/www/site/site. When unsure, add -n (--dry-run) and read the file list before anything is written.

2. Mirror with --delete

rsync -avzn --delete ./site/ user@SERVER_IP:/var/www/site/   # dry run first
rsync -avz --delete ./site/ user@SERVER_IP:/var/www/site/

--delete removes destination files that no longer exist in the source. A real mirror needs it, and it is also what turns a wrong trailing slash into a wiped directory, so dry-run every new --delete command once.

3. Exclude files

rsync -avz --exclude='node_modules/' --exclude='.git/' --exclude='*.log' ./app/ user@SERVER_IP:/srv/app/
rsync -avz --exclude-from='.rsyncignore' ./app/ user@SERVER_IP:/srv/app/

Patterns are relative to the transfer root; a leading slash anchors them, so /config.php excludes only the top-level file. --exclude-from reads one pattern per line.

4. Resume a partial transfer, custom port and key

rsync -avP -e 'ssh -p 2222 -i ~/.ssh/id_ed25519' ./disk.img user@SERVER_IP:/backup/

-P is short for --partial --progress: if the connection drops, the partial file is kept and re-running the same command continues from where it stopped. SSH options go through -e, and here the port is lowercase -p because it is ssh's flag, not rsync's; a Host entry in ~/.ssh/config removes the whole -e string.

sftp: interactive transfers over the same SSH login

sftp gives you an FTP-style prompt over SSH, handy for browsing before copying.

sftp -P 2222 -i ~/.ssh/id_ed25519 user@SERVER_IP
sftp> ls -l /var/www
sftp> lcd ~/Downloads
sftp> get /var/log/syslog
sftp> put -r ./site /var/www/
sftp> reget backup.iso
sftp> bye

Commands starting with l act on your machine (lcd, lls, lpwd); the rest act on the server. put -r and get -r copy directories, reget and reput resume an interrupted file, and !command runs a local shell command without leaving the session. sftp -b commands.txt user@SERVER_IP replays a file of these commands for cron jobs.

Copy files from Windows: PowerShell scp and WinSCP

Windows 10 (1809 and later) and Windows 11 ship the OpenSSH client, so scp, sftp and ssh exist in PowerShell with the same flags as on Linux:

cd C:\Users\you\Projects
scp -P 2222 -i $HOME\.ssh\id_ed25519 .\site.zip user@SERVER_IP:/home/user/
scp -r user@SERVER_IP:/var/www/site $HOME\Downloads

Keys live in C:\Users\you\.ssh\ next to known_hosts, and ssh-keygen -R SERVER_IP works there too. Relative paths after a cd sidestep any confusion between a drive letter and a hostname. rsync is not included; the cleanest way to get it is WSL: wsl rsync -avz /mnt/c/Users/you/site/ user@SERVER_IP:/var/www/site/.

For a GUI, WinSCP is the usual choice: it speaks SFTP and SCP, offers a two-pane drag-and-drop view, and its Synchronize command does roughly what rsync --delete does. It expects keys in PuTTY's .ppk format and offers to convert an OpenSSH key when you select one. For anything deployed repeatedly, a one-line scp or rsync in a script is easier to review and automate.

scp vs rsync vs sftp: which one to use

TaskBest toolWhy
One file, one timescpShortest command, nothing to install
A directory, first copyscp -r or rsync -aEquivalent result
Repeated deploys or backupsrsyncOnly changed data is sent
Mirror with deletionsrsync --deletescp cannot remove files
Resume a large filersync -P or sftp regetscp restarts from zero
Browse, then pick filessftp or WinSCPInteractive listing
Thousands of small filesrsync or tar over sshPer-file overhead dominates scp
Windows without WSLPowerShell scp or WinSCPNo native rsync

Rule of thumb: scp when you can type the command from memory, rsync as soon as you will run it twice.

Speed tips that actually move the needle

  • Compression is not free. -C (scp) and -z (rsync) shrink text, SQL dumps and logs, but waste CPU on tarballs, images and video. If top shows sshd pinned at 100% during a transfer, drop the flag.
  • Bundle many small files. Per-file round trips dominate when you push a source tree; one stream is faster: tar czf - ./site | ssh user@SERVER_IP 'tar xzf - -C /var/www/'.
  • Try another cipher on fast links. When the ssh process itself is the bottleneck, scp -c aes128-gcm@openssh.com uses the AES instructions built into nearly every modern CPU.
  • Protect long transfers. Run them inside tmux or screen so a dropped session does not kill the copy, or use rsync -P and re-run.
  • Verify and throttle. sha256sum file on both ends should match; scp -l or rsync --bwlimit keeps a backup from starving the application.

Permissions, ownership and common errors

Permission denied on the destination path

scp: /var/www//html/: Permission denied means the login succeeded but that user cannot write there. Copy into your home directory and sudo mv the files, give the user ownership of the target (sudo chown -R user:user /var/www/site), or with rsync add --rsync-path="sudo rsync", which works when the login user has passwordless sudo, as set up in creating a user with sudo rights on Linux. Uploading as root avoids the error but leaves root-owned files that a web server cannot write: sudo chown -R www-data:www-data /var/www/site afterwards.

Permission denied (publickey)

The server refused authentication. Check the username, force the key with -i, add -v; on the VPS, sudo journalctl -u ssh -n 30 names the reason.

Remote host identification has changed

Expected after a reinstall or an IP reassignment: the host key no longer matches ~/.ssh/known_hosts. If you did rebuild the server, run ssh-keygen -R SERVER_IP and reconnect. If you did not, find out why before typing a password anywhere.

No such file or directory

scp does not create parent directories: run ssh user@SERVER_IP 'mkdir -p /var/www/site' first. rsync creates the last directory component only (--mkpath, rsync 3.2.3+, creates parents).

rsync: command not found, or protocol data stream (code 12)

rsync is missing on one side; sudo apt install rsync there and retry.

Uploaded scripts that refuse to run

Files that came from Windows have no execute bit and often carry CRLF line endings, which produces bad interpreter: No such file or directory. Fix both with chmod +x deploy.sh and sed -i 's/\r$//' deploy.sh; the shebang side is covered in what a .sh file is and how bash runs it. One last classic: scp file.txt user@SERVER_IP without a colon after the host is a local copy that silently creates a file named user@SERVER_IP.

All of this assumes a server you reach over SSH as root or a sudo user, where the transfer itself, not a quota, is the limit. A Linux VPS from rdp.monster is that kind of machine: full root access, dedicated CPU and RAM, unlimited (fair-use) bandwidth, no KYC, crypto accepted, from $8.99 per month, and the server is online about 10 seconds after payment confirms, so the first thing you run on it can be the scp from step one.

Frequently Asked Questions

Is the scp command deprecated?

scp has not been removed, but since OpenSSH 9.0 it transfers over the SFTP protocol because the original scp protocol was declared outdated.
The OpenSSH developers described the original scp/rcp protocol as outdated and hard to secure, and from OpenSSH 9.0 (2022) the scp command moves files over SFTP instead. The command, its flags and its user@host:path syntax are unchanged, so existing scripts keep working; only shell expansion inside remote paths behaves differently, and -O restores the legacy protocol for servers that need it. For new automation, rsync or sftp are the more capable choices, but scp itself is fine for one-off copies.

Can I use scp with a password instead of an SSH key?

Yes, if the server still allows password authentication; scp prompts for it exactly like ssh does.
scp uses whatever authentication ssh would use for that host. If sshd has PasswordAuthentication yes, scp prompts for the password and the copy proceeds; there is no flag to pass the password on the command line, and tools such as sshpass that do so leave it in your shell history. Hardened servers usually disable password login, in which case scp fails with Permission denied (publickey) until you install a key with ssh-copy-id. Keys are also what let scripts run unattended.

Does scp overwrite existing files without asking?

Yes: scp and rsync both replace files on the destination silently, so preview with rsync --dry-run when in doubt.
scp overwrites any existing file of the same name at the destination with no prompt and no option to skip. rsync also overwrites, but only when the source differs, and it gives you control: -n (--dry-run) prints what would change, --ignore-existing leaves files that already exist untouched, --update skips files that are newer on the destination, and --backup --backup-dir=DIR keeps the replaced versions. For anything irreplaceable, copy into a fresh directory and swap it in afterwards.

How do I copy files directly between two VPS servers?

Either run rsync or scp on one server targeting the other, or use scp -3 to route the copy through your own machine.
The fastest route is server to server: ssh into the source VPS and run rsync -avz /data/ user@OTHER_IP:/data/ (or scp -r) so the data never crosses your home connection. That requires the first server to hold a key the second one accepts. If you would rather not place keys on servers, scp -3 user@HOST_A:/path user@HOST_B:/path copies through your machine using only your local credentials, at the cost of downloading and uploading everything once each.

Adrien Roche, Infrastructure & Hosting Editor

Systems engineer with 10+ years operating Windows Server and Linux fleets. Adrien runs the rdp.monster infrastructure documentation and writes our guides on RDP, VPS hosting, server administration, networking and privacy tooling.

Register to our reseller program

Your information

If you have any question, contact us by clicking here !
Name(Required)
Enter your email address, you must have an account on dashboard.rdp.monster !

Your company

Enter your website address if you have one
Quickly explain how you're going to sell services to your customers. For example, talk to people on forums.

Even monsters love cookies!

We use cookies to enhance your browsing experience, serve personalized ads or content, and analyze our traffic. By clicking "Accept", you consent to our use of cookies.