rsync exclude-from Mac is the cleaner way to back up developer projects without copying node_modules, .git, virtual environments, build output, and cache folders every time. Instead of stuffing a long list of --exclude flags into every command, keep the rules in one reviewed file and pass it to rsync with --exclude-from.
rsync exclude-from Mac: the basic pattern
The short version is simple: create a plain text file containing one exclude pattern per line, then point rsync at it.
cat > ~/Developer/.rsync-dev-excludes <<'EOF'
node_modules/
.git/
.venv/
venv/
dist/
build/
coverage/
.DS_Store
EOF
rsync -avhn --delete \
--exclude-from="$HOME/Developer/.rsync-dev-excludes" \
~/Developer/my-app/ /Volumes/Backup/my-app/
In that command, -a preserves the normal file metadata you want for a backup, -v prints what would happen, -h makes sizes readable, and -n is the dry run. Keep -n in place until the output looks boring. Remove only the n when you are ready to run the real copy.
This workflow is especially useful on macOS because developer folders often move between local storage, external drives, and cloud-synced destinations. The destination might be an external SSD today and a clean cloud mirror tomorrow. The exclude file should travel with the backup command so the same generated junk stays out of every copy.
Why developer backups need an exclude file
Source code is small. Developer workspaces are not. A normal web project can contain a few megabytes of source and hundreds of megabytes of generated folders. A monorepo can produce millions of filesystem entries across package-manager stores, transpiled output, coverage reports, test artifacts, local databases, and editor caches.
Copying all of that creates three practical problems:
- Backups take longer than they should. Small files are expensive because each one needs metadata work, directory traversal, and sometimes permission checks.
- Cloud destinations get noisy. If the backup lands in iCloud Drive, Dropbox, Google Drive, or OneDrive, the cloud client now has to index and upload generated files that can be recreated from lockfiles.
- Restore gets harder to trust. Old
node_modules, staledistfolders, and cached build output can hide whether the restored project actually rebuilds from source.
--exclude-from turns a fragile one-off command into a repeatable filter for every developer backup.Create a reusable rsync exclude file for Mac projects
Start with the folders that are generated, large, or tied to the current machine. You can add project-specific rules later.
# ~/Developer/.rsync-dev-excludes
# JavaScript and frontend
node_modules/
.npm/
.pnpm-store/
.yarn/cache/
.next/cache/
.nuxt/
.svelte-kit/
.turbo/
.vite/
dist/
build/
coverage/
# Python
.venv/
venv/
__pycache__/
.pytest_cache/
.mypy_cache/
.ruff_cache/
# Ruby
.bundle/
vendor/bundle/
# JVM, Rust, and other build output
target/
.gradle/
out/
# Git internals and macOS metadata
.git/
.DS_Store
The controversial entry is .git/. Excluding it is right when the destination is a clean working-tree backup and GitHub, GitLab, or another remote is the source of truth for history. Including .git can be right when you need an offline clone that preserves branches, reflogs, hooks, and unpushed commits. Make that decision deliberately. Do not exclude .git just because a blog post says so.
For many teams, the safest split is: keep Git history in Git remotes, keep active work in a local folder, and back up the working tree plus lockfiles with generated folders excluded. That gives you a small destination that can be restored and rebuilt instead of a stale copy of yesterday's dependencies.
Test --exclude-from with a dry run
Never test a new exclude file with a real mirror. Use a dry run and itemized output first:
rsync -avnih --delete \
--exclude-from="$HOME/Developer/.rsync-dev-excludes" \
~/Developer/my-app/ /Volumes/Backup/my-app/
Read the output with three questions in mind:
- Are source files, tests, docs, config files, and lockfiles included?
- Are generated folders such as
node_modules,dist,.next/cache, andcoverageabsent? - If
--deleteis enabled, is the delete list limited to files you really want removed from the destination?
If an ignored folder still appears, check the exact pattern. A trailing slash means “directory.” A leading slash anchors the pattern to the transfer root. A rule such as /node_modules/ only matches a top-level folder in the transfer, while node_modules/ can match directories named node_modules lower in the tree. For most developer backups, the unanchored directory form is the useful default.
Also check the location of the exclude file itself. Shell expansion inside quotes can surprise people when they write --exclude-from='~/Developer/.rsync-dev-excludes'. The single quotes prevent ~ from expanding. Use $HOME inside double quotes or the full absolute path.
Exclude-from patterns that match the way rsync works
rsync patterns are not regular expressions. Treat them as path-matching rules relative to the transfer root.
Good default patterns
node_modules/skips directories with that name wherever they appear..next/cache/skips the noisy cache but can still copy useful Next.js config files.coverage/skips test reports that should be regenerated.*.logskips log files when they are not part of your source of truth.
Patterns to review carefully
*.envcan protect secrets, but it can also omit example config needed for restore..git/is right for clean working-tree backups, wrong for full offline clone backups.build/may skip generated output, but some projects keep checked-in build assets.*cache*is usually too broad for a shared exclude file.
When in doubt, make the rule narrower. Excluding too little costs time. Excluding too much can create a backup that looks clean but cannot be restored without missing files.
A safe Mac backup command using --exclude-from
For a local mirror to an external drive, this is a solid starting point:
EXCLUDES="$HOME/Developer/.rsync-dev-excludes"
SOURCE="$HOME/Developer/my-app/"
DEST="/Volumes/DevBackup/my-app/"
rsync -avnih --delete \
--exclude-from="$EXCLUDES" \
"$SOURCE" "$DEST"
After reviewing the output, run the same command without n:
rsync -avih --delete \
--exclude-from="$EXCLUDES" \
"$SOURCE" "$DEST"
Keep the source trailing slash if you want the contents of my-app copied into the destination. Remove it only when you intentionally want the folder itself copied into a parent directory. This small difference is responsible for a lot of confusing nested backups.
If the destination is inside a cloud-synced folder, let the local copy finish before judging the cloud state. rsync can complete while iCloud Drive or another client is still uploading the changed destination. That is another reason to keep the destination small: the cloud client should see source files and lockfiles, not a dependency explosion.
When a GUI sync app is easier than maintaining rsync commands
rsync --exclude-from is excellent if you are comfortable owning the command. It is scriptable, fast, and transparent. It also expects you to maintain paths, dry runs, schedules, logs, and failure handling yourself.
If you mainly want a repeatable Mac backup that skips developer junk, a focused GUI can be simpler. Lsyncer is built for this exact workflow: choose source and destination folders, keep generated folders such as node_modules, .git, virtual environments, build output, and caches out of the sync, then run the copy on demand or on a schedule. It is a one-time $19.99 macOS app, not a subscription.
The practical rule is straightforward: use rsync when you want a command you can audit and automate in your own scripts. Use Lsyncer when you want the same filtered-backup idea with a native interface, visible status, and less shell maintenance. In both cases, the winning move is the same: sync source, not disposable build products.
Best practices for rsync exclude files on macOS
- Keep one shared developer exclude file. Start with broad generated-folder rules, then add project-specific files only when needed.
- Use dry runs after every rule change. A rule that looks harmless can remove a required folder from your backup.
- Keep active projects outside cloud folders. Work in
~/Developeror~/Code, then sync a filtered copy to the place you want backed up. - Do restore drills. Copy the backup to a scratch folder and run
npm ci,pnpm install --frozen-lockfile,pip install -r requirements.txt, or your stack's equivalent. - Do not sync secrets by accident. Review
.env, local database dumps, private keys, and production credentials separately. A backup destination is not automatically a safe secret store.
Related reading
- Rsync dry run on Mac — preview copy and delete behavior before trusting a mirror command.
- Backup Node.js project on Mac — decide what to save, what to regenerate, and how to restore-test a clean project backup.
- Backup tool for Mac developers — see where rsync fits alongside Git, Time Machine, and filtered GUI sync.
- Rsync exclude node_modules on Mac — more detail on dependency-folder rules for JavaScript projects.
- File sync for Mac developers — compare Finder, rsync, iCloud, and filtered app workflows.
FAQ
How do I use rsync exclude-from on Mac?
Create a text file with one exclude pattern per line, then run rsync --exclude-from="/path/to/file" source/ destination/. For backups, test with -n first: rsync -avhn --exclude-from="$HOME/Developer/.rsync-dev-excludes" ~/Developer/my-app/ /Volumes/Backup/my-app/.
Should I exclude node_modules from rsync backups?
Usually yes. node_modules is generated from package.json and a lockfile, and it contains thousands of files that slow backups and cloud sync. Back up the manifests and lockfiles, then reinstall dependencies after restore.
Should an rsync exclude file include .git?
It depends on the goal. Exclude .git/ for a clean working-tree backup when your Git remote holds history. Include .git if the backup must preserve local branches, unpushed commits, hooks, or a full offline clone.
Why is my rsync exclude-from file not working?
Common causes are quoting ~ so it does not expand, using a pattern anchored to the wrong transfer root, forgetting the trailing slash for directory rules, or testing against a different source path than the one the pattern expects. Run an itemized dry run with -avnih and inspect whether the unwanted paths still appear.
Can I use rsync exclude-from with iCloud Drive?
Yes, if you are using rsync to copy a filtered project into an iCloud Drive destination. iCloud itself does not read rsync exclude files; rsync performs the filtering before files reach the destination. Keep active projects local and sync a clean copy into iCloud if you want cloud availability.