How to Copy Files Between VPS with SCP and Rsync — Practical Guide

Learn how to copy files between VPS, from your local machine, or between remote servers using SCP and Rsync with practical examples and useful flags.

Transferring files between servers from the terminal is one of the most common operations in the day-to-day life of anyone managing a VPS. Whether you are pushing code to production, moving backups between regions, or migrating data between machines, SCP and Rsync are the standard tools — both run over SSH and do not require an extra daemon to be installed in most cases.

The choice between the two is rarely “which is better” and more often “which fits the scenario”. SCP shines in simple one-off transfers, typically a single file or small directory copied once. Rsync shines in anything recurring, in large volumes, or where the network can fail and you need to resume.

This guide shows the exact commands for three scenarios: local → VPS, VPS → local, and VPS → VPS. Estimated time to read and run the examples: 15-20 minutes.

Prerequisites

Before you start

You need working SSH access to both machines involved. If you have not set up SSH keys yet, do that first — copying files while typing a password on every connection becomes unworkable fast. The examples assume Linux or macOS on the client; on Windows, use WSL2 or PowerShell with OpenSSH enabled.

Typical technical data to fill into the commands:

User root or your sudo user
SSH port 22 (default)
Host VPS IP or hostname

Confirm basic access before attempting to transfer:

ssh [email protected]

If login works without prompting for a password (or only asks for the key passphrase), you are ready.

Copying with SCP — basic syntax

SCP follows the pattern scp [options] source destination. Source or destination can be local (a normal path) or remote (user@host:path).

01

Copy a local file to the VPS:

scp ./backup.tar.gz [email protected]:/var/backups/

./backup.tar.gz is the local file. The remote destination is the directory /var/backups/ on the VPS. The trailing slash on the destination matters — without it, the file would be renamed to backups.

02

Copy an entire directory recursively:

scp -r ./my-project [email protected]:/var/www/

The -r flag enables recursive mode. Without it, SCP refuses directories. The my-project directory will be created inside /var/www/ at the destination.

03

Download a file from the VPS to the local machine:

scp [email protected]:/var/log/nginx/access.log ./

Direction is defined by the order of arguments. ./ indicates the current directory on the client.

04

Copy directly between two VPS using the -3 flag:

scp -3 [email protected]:/data/db.sql [email protected]:/tmp/

Without -3, SCP tries to establish SSH between the two VPS, which requires them to have keys registered on each other. With -3, data flows through your local client acting as a router — slower, but works without extra configuration.

Useful SCP flags for daily use

  • -P 2222 — specifies the SSH port (capital P in SCP, different from the lowercase -p in SSH)
  • -p (lowercase) — preserves timestamps and modes of the original file
  • -q — quiet mode, suppresses the progress bar (useful in scripts)
  • -l 5000 — caps bandwidth in kbit/s, avoids saturating the VPS link
  • -i ~/.ssh/specific_key — uses a specific SSH key instead of the default
SCP was marked as legacy by OpenSSH

Starting with OpenSSH 9.0, the SCP protocol uses SFTP under the hood for security reasons. The commands keep working identically, but if you see messages about “legacy protocol”, know that it is not an error — it is informational. For new integrations, consider SFTP or Rsync.

Copying with Rsync — efficiency at scale

Rsync uses a diff algorithm that compares source and destination block by block. On a repeated sync, it transfers only what changed — a huge gain on large directories.

01

Sync a local directory to the VPS:

rsync -avz ./site/ [email protected]:/var/www/site/

The flags mean:

  • -a (archive): preserves permissions, owners, timestamps, symbolic links, recursive
  • -v (verbose): shows files being transferred
  • -z (compress): compresses data in transit, reduces bandwidth by ~30-60% for text files

Watch the trailing slash on the source: ./site/ copies the contents of the directory. Without the slash (./site), it would copy the site directory itself into the destination, creating /var/www/site/site/.

02

Add a progress bar and automatic resume:

rsync -avzP ./backup.tar.gz [email protected]:/backups/

The -P flag is shorthand for --partial --progress. It shows speed, ETA, and keeps partial files in case of interruption — just run the same command again to resume.

03

Exclude unnecessary directories:

rsync -avzP \
  --exclude='node_modules' \
  --exclude='.git' \
  --exclude='*.log' \
  ./project/ [email protected]:/var/www/project/

Each --exclude accepts glob patterns. You can also use --exclude-from=file.txt to load a list at once.

04

Sync between two VPS by running the command inside one of them:

ssh [email protected] \
  "rsync -avzP /data/ [email protected]:/data/"

Here Rsync runs inside vps1, avoiding the bottleneck of going through your client. It requires vps1 to have an SSH key registered on vps2.

Use --dry-run before big operations

Add --dry-run (or -n) to simulate the operation without transferring anything. It pairs well with -v to see exactly what would be copied and deleted. Essential before using --delete, which removes files at the destination that do not exist at the source.

Rsync flags that save time

FlagWhat it does
--deleteRemoves files at destination that disappeared from source — true sync
--bwlimit=5000Caps bandwidth in KB/s
--checksumCompares via checksum (slower, safer than timestamp)
-e "ssh -p 2222"SSH on a non-standard port
--numeric-idsPreserves uid/gid numerically, useful in backups
--append-verifyResumes a partial file, validating the checksum of blocks already sent

Verifying after the transfer

Confirming that files arrived intact is part of the process. Compare counts and checksums.

01

Count files at source and destination:

# Local
find ./project -type f | wc -l

# Remote
ssh [email protected] "find /var/www/project -type f | wc -l"

The numbers should match exactly, minus any exclusions applied.

02

Compare the checksum of a critical file:

# Local
sha256sum backup.tar.gz

# Remote
ssh [email protected] "sha256sum /backups/backup.tar.gz"

Identical hashes confirm bit-for-bit integrity.

Troubleshooting

Permission denied (publickey)

Your SSH key is not being accepted. Check that the public key is in ~/.ssh/authorized_keys on the server and that permissions are correct (700 on the .ssh directory, 600 on the file). Test with ssh -v user@host to see the full handshake and identify which key was offered.

No space left on device

The destination is out of space. Check with df -h on the server and free up space before trying again. Rsync with --partial keeps the partial file, so resuming works after you free up space.

Connection timed out during a long transfer

The SSH connection expired. Add ServerAliveInterval 60 to your ~/.ssh/config or use rsync -e "ssh -o ServerAliveInterval=60" to send keepalives. In transfers that last hours, this prevents intermediate NAT from dropping the session.

Next steps

With SCP and Rsync mastered, you cover 90% of day-to-day transfers. To go further:

  • Set up SSH keys with passphrase + SSH agent for productivity
  • Automate backups with Rsync via cron, writing to /etc/cron.d/
  • Explore rsnapshot for incremental backups with hardlinks
  • Consider SSHFS to mount remote directories as a local filesystem
  • For transfers between geographically distant regions, evaluate tools like bbcp or mbuffer that parallelize streams

If you are putting this into production, a Hostini VPS already ships with hardened SSH by default and a network link sized to sustain long transfers without throttling — useful for both deploys and recurring backups.

Frequently asked questions

What is the practical difference between SCP and Rsync?

SCP copies everything at once without comparing the destination — simple, but it re-sends the entire file if you run it again. Rsync compares source and destination block by block, transfers only the diff, and supports resume. For small, one-off files, SCP is faster to type. For large directories or recurring syncs, Rsync always wins.

Can I copy files from one VPS directly to another without going through my computer?

Yes. Both SCP and Rsync accept remote source and destination on the same line, e.g. `scp user1@vps1:/path/file user2@vps2:/path/`. The local client acts as a control intermediary, but data can flow directly if you use `-3` with SCP or run the command inside one of the VPS over SSH.

How do I resume an interrupted Rsync transfer?

Use the flags `--partial --append-verify`. The `--partial` flag keeps the partial file at the destination, and `--append-verify` resumes from where it stopped while validating the checksum of blocks already sent. Combined with `-P` (which is `--partial --progress`), you get resume plus a progress bar.

Is it safe to use the --exclude flag to ignore large folders?

Yes, and it is recommended. Always exclude `node_modules`, `vendor`, `.git`, `*.log` and caches that can be rebuilt at the destination. Example: `rsync -avz --exclude='node_modules' --exclude='.git' src/ dest/`. It drastically reduces the volume transferred without losing project integrity.

Why is my SCP transfer extremely slow?

SCP uses the AES-128-CTR cipher by default, which is secure but CPU-intensive. On a VPS with a fast link and limited CPU, encryption becomes the bottleneck. Try `scp -c [email protected]` or switch to Rsync over SSH, which supports compression (`-z`) and a more efficient protocol for multiple files.

How do I copy files preserving permissions and ownership?

With SCP, use the `-p` flag to preserve timestamps and modes. With Rsync, use `-a` (archive mode), which already includes preservation of permissions, owners, groups, timestamps, and symbolic links. To preserve uid/gid numerically instead of mapping by name, add `--numeric-ids` to Rsync.

Topics:
Next steps Ryzen cloud with NVMe storage and always-on DDoS protection.Go live on a Hostini VPS →
Was this tutorial helpful?
Chat on WhatsApp