rsync exclude-from Mac: Reusable Exclude Files for Developer Backups

rsync exclude-from Mac guide: build reusable exclude files for developer backups that skip node_modules, caches, and build output.

Mac developer reviewing an rsync exclude file before backing up a code project

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, stale dist folders, and cached build output can hide whether the restored project actually rebuilds from source.
One exclude file, cleaner project backups Mac project folder src/ package.json README.md node_modules/ dist/ Exclude file node_modules/ .git/ dist/ build/ coverage/ Backup src/ package.json README.md generated files skipped The exclude file keeps policy separate from the sync command, so it is easier to review and reuse.
--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.

Generated project folders crowding a narrow backup pipeline before filters are applied
Without exclusions, a backup spends most of its time on the files you are least likely to need after a restore.

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:

  1. Are source files, tests, docs, config files, and lockfiles included?
  2. Are generated folders such as node_modules, dist, .next/cache, and coverage absent?
  3. If --delete is 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.
  • *.log skips log files when they are not part of your source of truth.

Patterns to review carefully

  • *.env can 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.

Organized developer backup workflow with clean source files moving to an external drive
The clean workflow copies meaningful project files and leaves regenerated dependencies on the development machine.

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 ~/Developer or ~/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.

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.