How to sync files and directories with rsync - Rocketeers app

  [ Rocketeers ](/)   

[Login](https://rocketeersapp.com/login) 

 On this page

 Knowledge
---------

How to sync files and directories with rsync
============================================

### [\#CommandLine](https://rocketeersapp.com/knowledge/command-line)

Learn how to sync files and directories with rsync, including syncing over SSH, dry runs, deleting, excluding, and the trailing-slash gotcha.

 Published by [Mark van Eijk](https://rocketeersapp.com/author/mark-van-eijk) on June 23, 2026 · 3 minute read

1. [Why rsync instead of scp](#content-why-rsync-instead-of-scp)
2. [The basic local sync](#content-the-basic-local-sync)
3. [The trailing-slash gotcha](#content-the-trailing-slash-gotcha)
4. [Syncing over SSH](#content-syncing-over-ssh)
5. [Always dry-run first](#content-always-dry-run-first)
6. [Mirroring with --delete](#content-mirroring-with---delete)
7. [Excluding files](#content-excluding-files)
8. [A practical deploy command](#content-a-practical-deploy-command)

[\#](#content-why-rsync-instead-of-scp "Permalink")Why rsync instead of scp
---------------------------------------------------------------------------

When I need to move a directory between machines, my first instinct used to be `scp`. It works, but it copies everything every single time. The moment I started syncing a large project folder repeatedly, that got painfully slow.

`rsync` is smarter. It compares source and destination and only transfers the parts that actually changed (the deltas). The first sync is a full copy, but every sync after that is fast because it skips files that are already identical. That makes it perfect for backups and deploys. If you only need a one-off file copy, [scp](/copy-files-over-ssh-scp) is still fine, but for anything you run more than once, reach for rsync.

[\#](#content-the-basic-local-sync "Permalink")The basic local sync
-------------------------------------------------------------------

The command I use most is short:

 ```
rsync -av source/ destination/

```

The flags:

- `-a` is archive mode. It's a bundle that preserves permissions, timestamps, symlinks, and ownership, and recurses into subdirectories. This is what you want 95% of the time.
- `-v` is verbose, so you see which files are being transferred.

Add `-h` if you want human-readable sizes in the output (`-avh`).

[\#](#content-the-trailing-slash-gotcha "Permalink")The trailing-slash gotcha
-----------------------------------------------------------------------------

This one bites everyone once. A trailing slash on the **source** changes the meaning:

 ```
# Copies the CONTENTS of source/ into destination/
rsync -av source/ destination/

# Copies the source FOLDER itself into destination/ (destination/source/)
rsync -av source destination/

```

With the slash, you get `destination/file.txt`. Without it, you get `destination/source/file.txt`. When something ends up one level too deep, this is almost always why.

[\#](#content-syncing-over-ssh "Permalink")Syncing over SSH
-----------------------------------------------------------

rsync runs over SSH by default for remote paths, so syncing to a server is straightforward:

 ```
rsync -avz -e ssh local/ user@host:/var/www/app/

```

- `-z` compresses data during transfer, which helps a lot over slower connections.
- `-e ssh` sets SSH as the transport. You can pass options here too, like a custom port: `-e "ssh -p 2222"`.

Pulling from a server to your machine is just the two paths swapped:

 ```
rsync -avz user@host:/var/www/app/ ./backup/

```

If you haven't set up key-based access yet, see [connecting to a server with SSH](/connect-to-server-ssh-command) first so you're not typing a password on every sync.

[\#](#content-always-dry-run-first "Permalink")Always dry-run first
-------------------------------------------------------------------

Before any sync that deletes or overwrites, I run it with `--dry-run` (or `-n`). It shows exactly what would happen without touching a single file:

 ```
rsync -av --dry-run source/ destination/

```

Pair it with `--progress` once you're happy and want to watch a real transfer:

 ```
rsync -av --progress source/ destination/

```

[\#](#content-mirroring-with---delete "Permalink")Mirroring with --delete
-------------------------------------------------------------------------

By default rsync never removes files from the destination. If you want a true mirror, where files deleted from the source also disappear from the destination, add `--delete`:

 ```
rsync -av --delete source/ destination/

```

This is powerful and dangerous. Run it with `--dry-run` first, every time. A misplaced trailing slash plus `--delete` is how people wipe a backup.

[\#](#content-excluding-files "Permalink")Excluding files
---------------------------------------------------------

You rarely want to sync everything. Skip `node_modules`, `.git`, and friends with `--exclude`:

 ```
rsync -avz --exclude '.git' --exclude 'node_modules' --exclude '*.log' \
  ./app/ user@host:/var/www/app/

```

For longer lists, put patterns in a file and use `--exclude-from`:

 ```
rsync -avz --exclude-from='.rsyncignore' ./app/ user@host:/var/www/app/

```

[\#](#content-a-practical-deploy-command "Permalink")A practical deploy command
-------------------------------------------------------------------------------

Putting it together, here's the kind of one-liner I keep around for deploying a build:

 ```
rsync -avz --delete --progress \
  --exclude '.git' --exclude '.env' \
  ./public/ user@host:/var/www/app/public/

```

Run it once with `--dry-run` appended, confirm the file list, then run it for real. Once you've done that a couple of times, rsync becomes the tool you never think twice about.

### Subscribe to our newsletter

Do you want to receive regular updates with fresh and exclusive content to learn more about web development, hosting, security and performance? Subscribe now!

  Fill in your email address to receive updates  Subscribe 

#### More in [\#CommandLine](https://rocketeersapp.com/knowledge/command-line)

- [Argument list too long (Bash: /bin/rm)](https://rocketeersapp.com/knowledge/argument-list-too-long)
- [How to install Composer packages locally](https://rocketeersapp.com/knowledge/install-composer-packages-locally)
- [How to send GET and POST requests with curl](https://rocketeersapp.com/knowledge/curl-post-get-api-requests)
- [Essential Linux command line basics for developers](https://rocketeersapp.com/knowledge/linux-command-line-basics)
- [How to search file contents with grep](https://rocketeersapp.com/knowledge/search-files-grep-command)
- [The complete guide to the curl command](https://rocketeersapp.com/knowledge/curl-command-complete-guide)

 [View all 21 articles →](https://rocketeersapp.com/knowledge/command-line)
