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

Source: https://rack4.io/tutorials/transfer-files-to-vps · Updated: 2026-11-12

Difficulty: beginner · Time: about 15 minutes · Tested on: Ubuntu 24.04 LTS

Prerequisites:

- 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](https://rack4.io/help/connect-with-ssh). On Windows, a key file other users can read is ignored (`bad permissions`); the [SSH tunnel tutorial](https://rack4.io/tutorials/ssh-tunnel-private-service#if-it-doesn-t-work) 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.
