Transfer files to and from your VPS with scp, SFTP and rsync

Copy files and folders between your computer and your server over SSH, from Windows, macOS or Linux, resume a big transfer and check that it arrived intact.

Tested on Ubuntu 24.04 LTS

Before you start

  • A server you can log in to with SSH
  • An SSH client on your computer (built into Windows 10 and 11
  • macOS and Linux)

Your server already accepts file transfers: scp, SFTP and rsync all run over SSH, with the same address, user and key you log in with. There's no FTP server to install and no extra port to open.

  • scp: copy a file or folder in one command. Built into Windows, macOS and Linux.
  • SFTP: an interactive or graphical session (FileZilla, WinSCP, Cyberduck all speak it).
  • rsync: copies only what changed, and can resume an interrupted transfer. The best choice for big or repeated copies, from macOS, Linux, or another server.

What we tested: on 11 November 2026, from Windows 11 (its built-in OpenSSH 9.5 scp and sftp) and from Linux (OpenSSH 10.2, rsync 3.2.7) to an Ubuntu 24.04 server, with a non-root user, paths containing spaces, a 300 MB file whose transfer we stopped partway and resumed, and SHA-256 checks on both ends. On 12 November 2026 we tested the permission fixes for a static site and an app in an Ubuntu 24.04 container, starting from the owner-only permissions a Windows copy leaves. Every command below is from those runs.

The examples use deploy@203.0.113.42, a user called deploy on your server's address, and /var/www as the folder on the server. Use your own; root works the same way.

scp: copy a file or a folder

Upload a file to a folder on the server (the trailing / means "into this folder"):

bash
scp index.html deploy@203.0.113.42:/var/www/

Upload a folder with everything in it: add -r.

bash
scp -r "my site" deploy@203.0.113.42:/var/www/

Download a file to the folder you're in (.):

bash
scp "deploy@203.0.113.42:/var/www/my site/index.html" .
  • Spaces in paths: quote the whole argument, server part included, as above.
  • A different key: add -i path/to/key. A different SSH port: -P 2222 (capital P for scp and sftp, lowercase -p for ssh).

From Windows: check the permissions

Windows files carry no Linux permissions, and in our test, folders copied from Windows with scp -r arrived readable only by their owner (drwx------). A web server running as another user then answers 403 Forbidden. How to fix it depends on what you copied.

A static website (public files only). Run this on the folder the web server serves, after the copy:

bash
sudo find "/var/www/my site" -type d -exec chmod 755 {} +
sudo find "/var/www/my site" -type f -exec chmod 644 {} +

That gives folders 755 and files 644: you can write, and every user on the server, the web server included, can read. Only do this to a folder whose every file is meant to be public. Config files and secrets (.env, private keys, database passwords) don't belong in it: keep them outside the web root, owned by the user of the service that reads them, with chmod 600.

An application (code plus .env or keys). Don't make it readable by everyone. Let the service's own user read it through a group, and keep the secrets readable only by that user. Here the app runs as the user myapp from /srv/myapp, and deploy uploads it:

bash
sudo chown -R deploy:myapp /srv/myapp
sudo chmod -R u=rwX,g=rX,o= /srv/myapp
sudo chown myapp:myapp /srv/myapp/.env
sudo chmod 600 /srv/myapp/.env

In our test, after these commands www-data could read index.html in the static site, myapp could read its code and its .env, deploy could still update the code, and www-data got Permission denied on both the app's code and its .env.

Copies made from macOS or Linux keep the permissions they had.

SFTP: a session, or a graphical client

bash
sftp deploy@203.0.113.42

Then put file, get file, ls, cd and lcd (change folder on your computer), and exit. With quotes for spaces: put "my site/img/a b.bin" "copy b.bin".

In FileZilla, WinSCP or Cyberduck, choose SFTP (not FTP or FTPS), port 22, your user, and your private key file. The server address is the one in the panel.

rsync: big folders, repeated copies, resuming

rsync must be on both ends. If rsync --version doesn't answer on the server, install it: sudo apt install rsync on Ubuntu or Debian, sudo dnf install rsync on AlmaLinux or Rocky. On macOS and Linux it's usually there already. Windows doesn't include it: use scp from Windows, or run rsync inside WSL.

bash
rsync -a --partial --info=progress2 "my site/" deploy@203.0.113.42:/var/www/site/
  • -a keeps permissions, times and folders.
  • --partial keeps a half-copied file if the connection drops, so running the same command again continues it instead of starting over. In our test, a 300 MB transfer stopped after 80 MB; the rerun finished the rest, and the checksums matched.
  • The trailing slash matters: "my site/" copies what's inside the folder into /var/www/site/; "my site" without it creates /var/www/site/my site/.

Run it again any time: only files that changed are sent. To see what would change without copying anything, add --dry-run --itemize-changes.

Check it arrived intact

Compare a SHA-256 checksum on both ends. On the server:

bash
sha256sum "/var/www/site/big file.bin"

On your computer: sha256sum on Linux, shasum -a 256 on macOS, or in PowerShell Get-FileHash .\file.bin (or certutil -hashfile file.bin SHA256). The two long strings should be identical.

Who owns the files

Files arrive owned by the user you connected as. Upload as the user who will use them (for example deploy for a site it manages), and into a folder that user can write to. If you need a folder only root can write, upload to your home folder, then sudo mv it into place and sudo chown -R it.

Don't put private keys, .env files or database passwords in shared folders on the way: copy them straight to where they belong, and keep them readable only by the user who needs them (chmod 600).

If it doesn't work

  • Permission denied (publickey): the same login as SSH, so fix that first: Connect to your server with SSH. On Windows, a key file other users can read is ignored (bad permissions); the SSH tunnel tutorial has the one-line fix.
  • scp: dest open "/etc/file": Permission denied: the user can't write to that folder on the server. Upload to the home folder, or fix the folder's owner.
  • rsync: command not found: install rsync on the server (or the computer that's missing it).
  • A web page answers 403 after an upload from Windows: the permissions above.

What we didn't test

The graphical clients (FileZilla, WinSCP, Cyberduck), macOS's built-in tools, and transfers through a jump host.

Ready when you are

Order a server