Skip to content

How to Exclude Files and Directories with rsync

6 min read

Applies to: rsync 3.5.0 (Debian 13, AlmaLinux 10.2) and rsync 3.2.7 (Ubuntu 24.04)

To make rsync skip files or folders, add --exclude with a pattern, once per pattern:

rsync -av --exclude='cache/' --exclude='*.log' /var/www/site/ /backup/site/

Three things decide whether an exclude works: the pattern is matched against paths relative to the folder you copy (never the full path on disk), a trailing / means "directories only", and a leading / means "only at the top of the transfer". For many patterns, put them in a file and use --exclude-from. Always test with a dry run (-n) first.

Quick reference

  • --exclude='cache/': every folder named cache, at any depth, with its contents.
  • --exclude='cache': every file or folder named cache.
  • --exclude='/cache/': only the cache folder directly inside the source.
  • --exclude='wp-content/cache/': only that nested folder.
  • --exclude='cache/*': the contents of cache, but the empty folder is still created.
  • --exclude='*.log': all files ending in .log.
  • --exclude='.*': all hidden files and folders (.git, .env, .htaccess).
  • --exclude='my notes.txt': a name with spaces, in quotes.

We tested every pattern below on this lab folder:

site/.env
site/.git/HEAD
site/assets/cache          (a file, not a folder)
site/cache/page.html
site/error.log
site/index.php
site/logs/access.log
site/logs/keep.log
site/node_modules/pkg/index.js
site/wp-content/cache/x.html
site/wp-content/uploads/2026/photo.jpg

Exclude a folder (and get the path right)

  1. Write the pattern relative to the source. With site/ as the source, site/cache is /cache or cache in rsync terms. A full path does not match: --exclude='/tmp/lab/site/cache' excluded nothing in our test.
  2. Add a trailing / if you only mean folders. In the lab, --exclude='cache' also skipped the file assets/cache; --exclude='cache/' copied it and skipped only the two folders.
  3. Add a leading / if you only mean the top level. --exclude='/cache/' skipped site/cache but still copied wp-content/cache.
  4. Watch the trailing slash on the source. Without it (rsync -a site /backup/) the folder name becomes part of every path, so /cache/ matched nothing and we needed /site/cache/ instead. See how to use rsync to copy and sync folders for what the trailing slash does.

A pattern with a slash in the middle, such as wp-content/cache, has to match whole path parts: content/cache did not exclude wp-content/cache. Use ** to match any number of folders, for example --exclude='wp-content/**/2026' skipped wp-content/uploads/2026.

Exclude several files and folders

  • Repeat the option: --exclude='*.log' --exclude='node_modules/' --exclude='.git/'. Each --exclude takes exactly one pattern.
  • In Bash you can shorten that with braces, --exclude={'*.log','node_modules','.git'}, because Bash expands it into three options. This does not work in sh on Debian and Ubuntu (dash), and cron runs jobs with sh unless the crontab sets SHELL: in our test under sh nothing was excluded. Use separate options or a file in scripts and cron jobs.
  • Use an exclude file for longer lists. Blank lines and lines starting with # or ; are ignored:
    $ cat exclude.txt
    # build and cache files
    *.log
    cache/
    
    node_modules/
    .git/
    .env
    $ rsync -a --exclude-from=exclude.txt site/ /backup/site/
    Do not leave spaces at the end of a line: in our test a line cache/ (with a trailing space) excluded nothing.

Preview and debug with a dry run

  1. Add -n (--dry-run) and -v to see what would be copied without copying anything. Lab example:
    $ rsync -avn --exclude-from=exclude.txt site/ dst/
    sending incremental file list
    created directory dst
    ./
    config.php
    index.php
    my notes.txt
    assets/
    assets/app.js
    assets/cache
    logs/
    wp-content/
    wp-content/uploads/
    wp-content/uploads/2026/
    wp-content/uploads/2026/photo.jpg
    ...
    total size is 12  speedup is 0.03 (DRY RUN)
  2. Add --debug=FILTER to see which rule skipped each item:
    $ rsync -an --debug=FILTER --exclude='cache/' --exclude='*.log' site/ dst/
    [sender] hiding directory cache because of pattern cache/
    [sender] hiding file error.log because of pattern *.log
    [sender] hiding file logs/keep.log because of pattern *.log
    [sender] hiding file logs/access.log because of pattern *.log
    [sender] hiding directory wp-content/cache because of pattern cache/
    For rules read from --exclude-from, rsync 3.5.0 printed "because of pattern <rule from a file read earlier>" instead of the pattern.
  3. Remove -n and run the real copy.

Include some files and exclude the rest

rsync checks the rules in order and the first rule that matches wins, so put --include before the --exclude it should override.

  • Copy only PHP files and keep the folder structure:
    rsync -am --include='*/' --include='*.php' --exclude='*' site/ /backup/php/
    --include='*/' lets rsync walk into every folder, and -m (--prune-empty-dirs) drops the folders that end up empty. Without -m our copy had ten empty folders; with the order reversed (--exclude='*' first) it copied nothing at all.
  • Skip a folder's contents but keep one file:
    rsync -a --include='logs/keep.log' --exclude='logs/*' site/ /backup/site/
    This copied only logs/keep.log. With --exclude='logs/' instead, nothing in logs was copied, because rsync never looks inside an excluded folder.

Excludes and --delete

With --delete, rsync removes files from the destination that are gone from the source, but it does not delete excluded files there: in our test dst/cache survived --delete --exclude='cache/'. Add --delete-excluded if the destination should lose them too. Our run then printed:

deleting cache/page.html
deleting cache/
deleting index.php
./
deleting wp-content/cache/x.html
deleting wp-content/cache/

Run any --delete command with -n first.

Excludes over SSH, .gitignore and -C

  • Remote copies use exactly the same patterns, still relative to the source folder: rsync -av --exclude='cache/' /var/www/site/ user@server:/backup/site/. For a custom SSH port, see rsync with a non-standard SSH port.
  • --filter=':- .gitignore' reads exclude patterns from .gitignore files in each folder. It skipped node_modules and the log files in our test. rsync reads the file with its own rules, not Git's, so keep it to simple patterns.
  • -C (--cvs-exclude) skips .git/, .svn/ and similar folders, but its built-in list also contains core, *.so, *.exe, *.bak and *.old, according to the rsync manual. Do not use it for full server or website backups.

"skipping directory" message

If rsync prints skipping directory site and copies nothing, you forgot -r or -a. Without one of them rsync does not copy folders. It still exits with code 0 in our test, so a script will not notice. Add -a (archive mode, which includes -r).

Related articles: how to use rsync to copy and sync files and directories, how to rsync only new or changed files, how to copy a folder recursively with scp and exclude files, 13 rsync command examples.

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Linux server management

hardening, patching, monitoring and automation for Ubuntu, Debian, RHEL and AlmaLinux.

Was this answer helpful?

Still stuck? Open a ticket