Rsync Operators and Keywords
Rsync operators and keywords give you fine-grained control over exactly which files are transferred. This page covers the special characters, wildcards, and filter rules that make rsync surgically precise.
Operators
Operators are special characters that rsync interprets in paths and patterns.
The Colon Operator (:)
The colon separates a remote hostname from a path:
# Format: user@host:path
rsync -avz /local/path/ user@server:/remote/path/
| Syntax | Meaning |
|---|---|
user@server:/path/ | Remote path via SSH |
/local/path/ | Local path (no colon) |
server::module | Rsync daemon connection (double colon) |
The Double Dash (--)
Terminates option parsing — everything after -- is treated as a path, not a flag. Useful when directory names start with a hyphen:
# Without -- rsync might interpret -mydir as a flag
rsync -av -- -mydir/ /backup/
# Safe way to handle unusual directory names
rsync -av -- ./--weird-name/ /destination/
Wildcards (Pattern Matching)
Rsync uses glob-style wildcards in --exclude, --include, and --filter patterns.
| Wildcard | Matches | Example | What It Matches |
|---|---|---|---|
* | Zero or more characters (not /) | *.log | error.log, access.log |
** | Zero or more characters including / | **/*.log | app/error.log, logs/2024/access.log |
? | Exactly one character | file?.txt | file1.txt, fileA.txt |
[...] | Any single character in the set | [0-9]*.sql | 1_dump.sql, 3_backup.sql |
[!...] | Any single character NOT in the set | [!.]*.conf | nginx.conf but not .hidden.conf |
Important: * vs **
This distinction catches many people off guard:
# Matches only in the current directory
--exclude '*.log' # Matches: error.log
# Does NOT match: logs/error.log
# Matches at any depth
--exclude '**/*.log' # Matches: error.log AND logs/error.log AND deep/path/error.log
Filter Keywords
--exclude — Skip Files/Directories
The most commonly used filter — tells rsync to skip matching files:
# Skip a specific directory
rsync -av --exclude 'cache/' /var/www/ /backup/www/
# Skip files by extension
rsync -av --exclude '*.log' --exclude '*.tmp' /var/www/ /backup/www/
# Skip build artifacts across all application types
rsync -av \
--exclude 'node_modules/' \
--exclude 'vendor/' \
--exclude '__pycache__/' \
--exclude '.git/' \
/var/www/app/ /backup/app/
--include — Force Inclusion
Include overrides a broader exclude. Order matters — rsync processes rules top to bottom:
# Sync ONLY PHP files (include PHP, exclude everything else)
rsync -av --include='*.php' --exclude='*' /var/www/ /backup/php-only/
# Sync only specific config files
rsync -av \
--include='*.conf' \
--include='*.yml' \
--include='*.yaml' \
--exclude='*' \
/etc/ /backup/configs/
Include/exclude rules are processed in order. The first matching rule wins. Always put --include before --exclude:
# Correct: includes first, then broad exclude
rsync -av --include='*.php' --exclude='*' /src/ /dest/
# Wrong: exclude matches everything before include is checked
rsync -av --exclude='*' --include='*.php' /src/ /dest/
--exclude-from and --include-from — Rules from Files
For complex or reusable filter sets, store patterns in a file:
# Build artifacts and dependencies
node_modules/
vendor/
__pycache__/
.git/
# Temporary and cache files
*.log
*.tmp
*.swp
cache/
tmp/
# Sensitive files
.env
*.pem
*.key
id_rsa*
rsync -av --exclude-from=/etc/rsync/web-excludes.txt /var/www/ /backup/www/
An exclude file is the cleanest approach when you have more than 3–4 exclude patterns. It keeps commands readable and makes patterns easy to maintain across multiple scripts.
--filter — Advanced Rule Syntax
The --filter flag provides more powerful rule processing:
| Filter Rule | Meaning | Example |
|---|---|---|
- PATTERN | Exclude | --filter='- cache/' |
+ PATTERN | Include | --filter='+ *.php' |
:- FILE | Read excludes from FILE | --filter=':- .gitignore' |
. FILE | Read merge-file rules | --filter='. /etc/rsync/rules' |
Using .gitignore as an Exclude Source
One of the most useful filter tricks for deploying code from a development directory:
# Automatically exclude everything listed in .gitignore
rsync -av --filter=':- .gitignore' /var/www/app/ user@prod:/var/www/app/
This is perfect for deployment workflows where .gitignore already excludes build artifacts, dependencies, and local configs.
Pattern Anchoring Rules
How rsync interprets the position of patterns:
| Pattern | Behavior |
|---|---|
cache/ | Matches any directory named cache at any depth |
/cache/ | Matches only cache at the root of the transfer |
*.log | Matches .log files in the immediate directory |
**/*.log | Matches .log files at any depth |
logs/ | Trailing / means match directories only, not files named logs |
Examples
# Exclude ALL cache directories at any depth
rsync -av --exclude 'cache/' /var/www/ /backup/
# Exclude ONLY the top-level cache directory
rsync -av --exclude '/cache/' /var/www/ /backup/
# Exclude .log files at any depth
rsync -av --exclude '**/*.log' /var/www/ /backup/
# Exclude directories only (not files with the same name)
rsync -av --exclude 'tmp/' /var/www/ /backup/
Practical Filter Combinations
Deploy Code Without Secrets or Build Artifacts
rsync -avz \
--exclude='.env' \
--exclude='.git/' \
--exclude='node_modules/' \
--exclude='vendor/' \
--exclude='*.log' \
/var/www/app/ user@production:/var/www/app/
Backup Only Database Dumps (SQL Files)
rsync -av \
--include='*.sql' \
--include='*.sql.gz' \
--exclude='*' \
/var/backups/ /mnt/external/db-backups/
Sync Configuration Files Only
rsync -av \
--include='*/' \
--include='*.conf' \
--include='*.yml' \
--include='*.yaml' \
--include='*.json' \
--include='.env.example' \
--exclude='*' \
/etc/ /backup/config/
--include='*/' Is NeededWhen combining includes and excludes, you need --include='*/' so rsync can descend into subdirectories. Without it, the broad --exclude='*' prevents rsync from entering any directory at all.
Sync Everything Except Large Media Files
rsync -av \
--exclude='*.mp4' \
--exclude='*.mov' \
--exclude='*.zip' \
--exclude='*.tar.gz' \
--max-size='50M' \
/var/www/uploads/ /backup/uploads/
Testing Patterns with Dry Run
Before applying any filter rules to production, always verify with --dry-run:
# Preview what will be transferred with your filters
rsync -av --dry-run \
--exclude='cache/' \
--exclude='*.log' \
/var/www/ /backup/www/
# Add --itemize-changes for more detail
rsync -av --dry-run --itemize-changes \
--exclude-from=/etc/rsync/excludes.txt \
/var/www/ /backup/www/
The --itemize-changes flag shows exactly what rsync would do for each file:
>f+++++++++— New file being sent>f.st......— File with changed size and timestamp*deleting— File that would be deleted (with--delete)
Common Pitfalls
| Pitfall | What Happens | Fix |
|---|---|---|
| Include after exclude | Include rule never triggers because exclude already matched | Put --include rules before --exclude |
Missing **/ prefix | Pattern only matches at root level | Use **/ to match at any depth |
Trailing / missing on directories | Matches files AND directories with that name | Add / to match only directories |
Not including */ with selective includes | Rsync can't descend into subdirectories | Add --include='*/' before your specific includes |
| Shell expanding wildcards | Shell resolves * before rsync sees it | Quote patterns: '*.log' not *.log |
Quick Reference
# Exclude a directory
--exclude 'cache/'
# Exclude by extension
--exclude '*.log'
# Include specific types, exclude rest
--include '*.php' --exclude '*'
# Load excludes from file
--exclude-from=excludes.txt
# Follow .gitignore rules
--filter=':- .gitignore'
# Match at any depth
--exclude '**/*.tmp'
# Anchor to transfer root
--exclude '/cache/'
# Always test first
rsync -av --dry-run --itemize-changes ...
What's Next
- Exclude and Include Patterns — Advanced filtering patterns and real-world examples
- Options and Flags — Complete flag reference
- Real-World Sync Patterns — Practical application sync behaviors