rsync --exclude / --include: Filtering Files
rsync --exclude skips files matching a pattern; --include re-adds files an exclude would have dropped.
CI deploys almost always need to exclude build junk like node_modules or .git. The catch is that include/exclude order matters: first match wins.
What it does
rsync evaluates filter rules top to bottom and uses the first matching rule for each file. --include re-includes a file before a broader --exclude can drop it, which is why includes generally come first. Patterns without a leading slash match at any depth; a leading slash anchors to the transfer root.
Common usage
# Exclude build junk from a deploy
rsync -av --exclude='.git/' --exclude='node_modules/' ./ user@host:/srv/app/
# Include only .js and .css, exclude everything else
rsync -av --include='*/' --include='*.js' --include='*.css' \
--exclude='*' src/ user@host:/srv/assets/Filter options
| Flag | What it does |
|---|---|
| --exclude=PATTERN | Skip files matching the pattern |
| --include=PATTERN | Re-include files an exclude would drop |
| --exclude-from=FILE | Read exclude patterns from a file |
| --include-from=FILE | Read include patterns from a file |
| --filter=RULE | General filter rule (merge, dir-merge, etc.) |
| -C / --cvs-exclude | Auto-exclude common VCS and editor cruft |
In CI
When using an include-only pattern (--include then --exclude="*"), you must also --include="*/" so rsync descends into subdirectories. Without it, rsync excludes the directories themselves and never reaches the files you wanted to keep.
Common errors in CI
A frequent surprise is that nothing transfers: with --exclude="*" listed before your --include rules, the first-match-wins logic excludes everything. Reorder so includes precede the catch-all exclude. Trailing-slash patterns like "node_modules/" match directories only.