# Rsync: Backing Up a Directory Over SSH

LLMS index: [llms.txt](/en/llms.txt)

---

## Rsync: Backing Up a Directory Over SSH

The classic way to copy a directory to a remote machine is `rsync` over SSH. No extra ports to open, traffic is encrypted, and the tool itself handles incremental transfers and metadata preservation. One command and the backup is ready.

---

## Basic rsync Command Over SSH

The minimal invocation to copy a local directory to a remote host:

```bash
rsync -avz /path/to/source/ user@host:/path/to/dest/
```

The trailing slash on `source` matters: without it, rsync creates a `source/` subdirectory on the remote side; with it, the contents go directly into `dest/`.

If SSH listens on a non-standard port:

```bash
rsync -avz -e 'ssh -p 2222' /path/to/source/ user@host:/path/to/dest/
```

> [!TIP]
> For cron automation, specify the port via `-e` rather than editing `/etc/ssh/ssh_config` — it's easier to maintain different hosts with different ports.

---

## Archive and Sync Flags

`-a` (archive) is the key flag. It bundles several options into one: recursive traversal, preservation of permissions, ownership, timestamps, symlinks, and empty directories.

| Flag | Purpose |
|------|---------|
| `-a` | Archive mode (recursion + metadata) |
| `-v` | Verbose output |
| `-z` | Compression during transfer |
| `-P` | `--partial --progress` — resume and progress bar |
| `--delete` | Remove files on receiver absent from source |
| `-e ssh` | Specify remote shell |
| `--bwlimit=KBPS` | Bandwidth limit in KB/s |

> [!WARNING]
> `--delete` is a powerful tool. If the source is accidentally cleared, the remote machine will be left empty. Verify the list before applying.

Full backup example with bandwidth limit and progress:

```bash
rsync -avzP --bwlimit=10000 -e 'ssh -p 2222' \
  /data/backup/ user@backupserver:/mnt/backup/
```

---

## File and Directory Exclusions

Use `--exclude` to omit specific paths. Patterns are relative to the source:

```bash
rsync -avz --exclude='*.log' --exclude='cache/' \
  /data/ user@host:/data/
```

When there are many exclusions, a file list is more convenient:

```bash
# exclude.txt
*.tmp
*.bak
cache/
lost+found/
```

```bash
rsync -avz --exclude-from='exclude.txt' /data/ user@host:/data/
```

> [!NOTE]
> `--exclude` is evaluated in order. Later rules can override earlier ones if paths overlap. For precise control, use `--filter`.

---

## Dry-Run Before Launch

Always run with `--dry-run` (or `-n`) first. rsync shows what would be copied or deleted without touching any files:

```bash
rsync -avzn --delete --exclude-from='exclude.txt' \
  /data/ user@host:/data/
```

Compare the output against the expected list. If it matches, remove `-n` and run the real sync.

For a cron job with email notifications:

```bash
#!/bin/bash
rsync -avz --delete --exclude-from='/etc/rsync-exclude.txt' \
  /data/ user@host:/data/ 2>&1 | mail -s "Rsync backup report" admin@example.com
```

---

## Summary

`rsync` over SSH covers 90% of backup tasks without additional agents on the receiver side. The essential order of operations: first `--dry-run`, then `--exclude`, then `--delete` if exact mirroring is needed. Everything else is host- and port-specific tuning.
