skip to content

Explain what a trailing slash on an rsync SOURCE path changes, using `rsync -a /var/www /backup/` versus `rsync -a /var/www/ /backup/`, and why getting it wrong is dangerous once `--delete` is added.

level: juniorimportance: must knowfreq 78%

answer

  1. one character changes the whole layout
  2. which side of the command reads it
  3. contents of, versus the directory itself
  4. a stray www/ appears in the destination
  5. --delete then judges everything else extraneous

basics

~20 s

A trailing slash on an rsync source means "copy this directory's contents"; without it rsync copies the directory itself. So /var/www/ fills /backup, while /var/www creates /backup/www. A trailing slash on the destination changes nothing.

solid answer

~40 s

rsync reads the trailing slash on the **source** only, and it means "the contents of this directory" rather than "this directory". `rsync -a /var/www /backup/` produces `/backup/www/...`; `rsync -a /var/www/ /backup/` puts the contents of `/var/www` straight into `/backup`. A slash on the destination makes no difference — when the source is a directory the destination is always treated as one. The danger appears with `--delete`, which removes anything in the destination that has no counterpart in the source. If you meant to fill `/backup` from `/var/www/` but dropped the slash, rsync creates `/backup/www` and then deletes everything else that was already in `/backup`, because relative to the new source those files are extraneous. I run any `--delete` command once with `-n -i` and read the itemised output before letting it run for real.

code

bash · 6 lines
bash
# Contents of /var/www land directly in /backup
rsync -ain --delete /var/www/ /backup/

# /var/www itself lands as /backup/www -- and everything
# already in /backup is now "extraneous" and would be deleted
rsync -ain --delete /var/www  /backup/

go deeper

for a junior

Memorise the rule and be able to say it cleanly: the slash belongs to the source and means "contents of". Show the two-line example with the extra www/ directory appearing, and mention that the destination slash is irrelevant.

for a middle

Explain what --delete actually compares — destination entries with no counterpart in the transferred source tree — and therefore why a missing slash turns a merge into a wipe. Mention that --delete needs recursion to do anything.

for a senior

Demonstrate the operating discipline: dry-run with -i before any destructive mirror, --max-delete as a circuit breaker, a sentinel-file check so an unmounted source cannot present as empty, and a recoverable destination rather than a bare mirror.

for a principal

Frame it as a blast-radius question. Argue where mirroring is acceptable at all versus versioned snapshots, who is allowed to run a --delete job unattended, and how you make the safe form the only form that exists in your tooling instead of relying on operator care.

## The rule, in one line rsync gives the **source** argument one special piece of syntax: a trailing `/` (or the equivalent `/.`) means *the contents of this directory*. Without the trailing slash, the argument means *this directory as an object*, and rsync recreates it by name underneath the destination. Nothing else about the command changes — same flags, same transport, same algorithm. One character decides the shape of the result. ## Worked example Assume `/var/www` contains `index.html` and `assets/`. ``` rsync -a /var/www /backup/ -> /backup/www/index.html /backup/www/assets/ rsync -a /var/www/ /backup/ -> /backup/index.html /backup/assets/ ``` The second form is what people usually mean when they say "mirror this directory to that one". The first form is what they usually type. ## The destination slash does not matter `/backup`, `/backup/` and `/backup/.` behave identically here. When the source is a directory, rsync treats the destination as a directory and creates it if the final component is missing (only the last component — rsync 3.2.3 added `--mkpath` for creating the whole path). Interviewers ask about the destination slash specifically to see whether you have internalised the rule or just memorised "put a slash on both". A good habit for the safe form is `rsync -a /var/www/. /backup/` — the explicit `.` is harder to lose to a shell completion or a copy-paste than a bare slash, and it means the same thing. ## What `--delete` compares `--delete` tells rsync to remove files in the destination that do not exist in the transferred source tree. It only applies to directories rsync is actually recursing into, so it needs `-r`/`-a` (or `-d`) to do anything, and it only judges the parts of the tree the transfer covers — a `--exclude`d path is *not* automatically deleted (that is what `--delete-excluded` is for). Now combine the two rules. You intended: ``` rsync -a --delete /var/www/ /backup/ # /backup becomes a copy of /var/www ``` and you typed: ``` rsync -a --delete /var/www /backup/ # /backup becomes a directory containing only www/ ``` In the second command the transferred source tree, as seen from the destination root, contains exactly one entry: `www`. Every other file in `/backup` — including the previous night's backups of other trees — is extraneous, so rsync deletes it. The destination is not corrupted or half-written; it is exactly what you asked for, which is why the mistake is so easy to make and so expensive. ## Making the mistake impossible - **Dry-run first, always, for anything with `--delete`.** `-n` (`--dry-run`) plus `-i` (`--itemize-changes`) prints one line per action, with `*deleting` lines for removals, and changes nothing. Read those lines; if you see deletions you did not expect, the slash is the first thing to check. - **Bound the blast radius** with `--max-delete=N`: rsync stops deleting once it hits the limit and exits with a non-zero status, so a mirror job that suddenly wants to remove ten thousand files fails loudly instead of succeeding destructively. - **Pin the source in a variable and write the slash once**, so the scheduled job cannot drift from the version you tested. - **Keep the destination recoverable.** A `--link-dest` snapshot chain, filesystem snapshots or object-store versioning means even a correct-but-unwanted deletion is undoable. A mirror is not a backup precisely because it faithfully replicates deletions. ## Two related traps in the same family The slash rule also applies to remote sources: `rsync -a host:/srv/data/ /local/` and `rsync -a host:/srv/data /local/` differ in exactly the same way, and the remote-side path is expanded by a shell on that host, so wildcards behave differently again. And `--delete` has one built-in safety you should know about: if rsync cannot read the source directory because of an I/O error, it aborts rather than treating the tree as empty (unless you pass `--ignore-errors`). That protection does **not** cover the common operational version of the accident — a backup source that is an *empty but successfully readable* mount point because the filesystem failed to mount. Then the source really is empty, rsync does what it was told, and `--delete` empties the destination. Guard the job by checking for a sentinel file in the source before you run it.

  • Does a trailing slash on the destination path ever change what rsync does?
    No. When the source is a directory, `/backup`, `/backup/` and `/backup/.` are equivalent — rsync treats the destination as a directory either way and creates the final component if it is missing. Only the source argument carries the contents-versus-directory meaning. (`--mkpath`, added in rsync 3.2.3, is what creates deeper missing destination components.)
  • How would you preview exactly what a `--delete` run is going to remove before running it?
    Run the identical command with `-n -i` added: `rsync -ain --delete SRC/ DST/`. `-n` performs no changes, and `-i` itemises every action, printing `*deleting` lines for removals and a per-attribute change string for updates. For a scheduled job, also set `--max-delete=N` so a real run aborts instead of proceeding if the deletion count is implausible.
  • Someone argues that a `--delete` mirror is their backup. What is wrong with that?
    A mirror replicates deletions and corruption faithfully — that is its job. If a file is removed or overwritten on the source, tonight's run removes or overwrites it on the destination, and yesterday's good copy is gone. A backup needs history: `--link-dest` snapshot chains, filesystem snapshots, or a versioned object store. Mirroring gives you availability, not recoverability.

saying these in an interview costs you the question

  • Thinks the destination trailing slash matters too
  • Says rsync always merges the source contents into the destination
  • Believes --delete removes files from the source side
  • Runs a --delete mirror without a dry-run first
  • Assumes rsync refuses to delete when a mount is missing

context