← One-Page Dev Guides

rsync trailing slash: what it actually copies

📅 2026-09-22 📊 beginner ["rsync" "linux" "backup"]

The Problem

It's Friday evening and the disk on the app server is at 95%. You run:

rsync -av --delete /srv/app/uploads/ /mnt/archive/uploads/

That one worked — the contents of uploads landed inside /mnt/archive/uploads/. So next week, migrating a server, you run what looks like the same command for the whole app directory:

rsync -av /srv/app /mnt/newserver/

and get /mnt/newserver/app/ — fine, that's what you wanted. But then you try to flatten a config dir into /etc/myapp/ and run rsync -av /srv/config /etc/myapp/, and now your systemd unit looks for /etc/myapp/config/settings.conf instead of /etc/myapp/settings.conf. The service fails to start on the new box. Nothing errored; rsync exited 0 both times. That's the worst part — there is no error message to grep for, just a destination tree with one extra (or one missing) directory level.

The reverse mistake is just as costly: you meant to back up the folder and its name (so you can restore multiple hosts side by side), used a trailing slash, and your --delete mirror quietly flattened two hosts' backups into one directory.

The Mistake

Treating source and source/ as the same argument. They're not. The trailing slash tells rsync "copy the contents of this directory", not "copy this directory."

The mental model is seductive because in every other shell context, a trailing slash is cosmetic. cd /var/log/ and cd /var/log are identical. Tab completion even adds the slash for you, so which one you end up typing is basically random. Your fingers have a decade of evidence that the slash doesn't matter, and rsync is the one tool that quietly breaks that assumption — for the source argument only, not the destination, which makes it even harder to internalize.

The Fix

# Copy the CONTENTS of /var/log into /backup/log/
rsync -av /var/log/ /backup/log/

# Copy the FOLDER /var/log into /backup/ (so you get /backup/log/)
rsync -av /var/log /backup/

Walk through it:

Before any destructive run (especially with --delete), do a dry run first:

# Preview exactly what would happen — transfers nothing
rsync -avn --delete /srv/app/uploads/ /mnt/archive/uploads/

-n (--dry-run) makes rsync print what it would do without touching the destination. It's one keystroke and it's the difference between "verified" and "hoped".

Why It Works

rsync uses the trailing slash as a "treat as directory contents" signal:

Under the hood, rsync builds a file list from each source argument before transferring anything. Without the slash, the argument names the directory itself, so the directory is the top item in the list and everything else nests under it. With the slash, rsync expands the argument into the directory's children — the file list starts one level down. The man rsync page states the equivalence directly: rsync -av /src/foo /dest and rsync -av /src/foo/ /dest/foo transfer the same files the same way. One character, decided entirely at file-list construction time, before a single byte moves.

This is the single most common rsync footgun. The man page calls it out in the first paragraphs; nobody reads it.

Copy-Paste

# Mirror /home to /backup, keeping folder structure
rsync -av --delete /home/ /backup/home/

# When in doubt, prepend -n to preview before you commit

Next Steps

Download PDF (1 page)

Get a fresh guide every Monday. £12/mo, £100/yr, or £250 lifetime.

See pricing →