rsync delete files not in source Mac is the command pattern you use when a backup destination should match the source, not just receive new files. The useful part is --delete. The dangerous part is also --delete: one wrong path, missing trailing slash, or broad exclude rule can remove files from the destination faster than you can read the terminal output.
rsync delete files not in source Mac: the safe command
The shortest version looks simple:
rsync -av --delete \
~/Developer/my-app/ \
/Volumes/DevBackup/my-app/
That command copies changes from the source folder into the destination folder and deletes destination files that no longer exist in the source. It is a mirror operation. If you removed src/old-api.ts from your project, the next run removes src/old-api.ts from the backup too. That is exactly what you want for a clean project mirror.
Do not run that version first. On a Mac developer workstation, the safer first command is an itemized dry run:
rsync -avnih --delete \
~/Developer/my-app/ \
/Volumes/DevBackup/my-app/
-akeeps recursive archive-style copy behavior.-vprints whatrsyncplans to do.-nmeans dry run, so no files are copied or deleted.-ishows itemized changes, including delete lines.-hmakes sizes easier to read.--deleteremoves destination files that are missing from the source when you run the real command.
Why --delete is useful and risky for developer backups
A plain rsync copy without --delete is additive. It updates changed files and creates new files at the destination, but it leaves old destination files alone. That can be fine for an archive, but it is not a clean mirror. After a few weeks, the destination may contain renamed files, old build output, deleted docs, stale generated bundles, and folders that no longer represent the project.
--delete fixes that drift. It says: after applying the source and filter rules, remove anything at the destination that should not be there. The destination becomes a current restore point instead of a pile of every file that ever existed.
The risk is that rsync follows paths literally. It does not know the difference between “my source folder is intentionally small” and “I accidentally pointed at the wrong empty folder.” It does not know that your external drive failed to mount and macOS created an ordinary directory under /Volumes/DevBackup. It does not know that a broad exclusion hid the files you wanted to keep. The only safety rail is the preview you read before the real run.
--delete makes a destination match the filtered source. That is powerful for mirrors and unforgiving when paths are wrong.Start with a delete preview, not a real delete
Use this review loop for any Mac project backup that removes destination-only files:
- Make sure the destination exists and is the destination you expect.
- Run
rsyncwith-n,-i, and--delete. - Search the output for
deleting. - Confirm every delete is expected stale state.
- Run the same command again after removing only
nfrom the flags.
# Preview first
rsync -avnih --delete \
~/Developer/my-app/ \
/Volumes/DevBackup/my-app/
# Real run only after review
rsync -avih --delete \
~/Developer/my-app/ \
/Volumes/DevBackup/my-app/
The difference between those two commands is one letter. Do not retype the whole command from memory. Edit the dry-run command so the paths, trailing slash, excludes, and delete behavior stay exactly the same.
Common dry-run output includes lines like this:
>f.st...... src/sync.ts
>f+++++++++ docs/recovery.md
deleting dist/old-bundle.js
deleting tmp/old-export.json
The deleting lines are the review target. A few old generated files may be fine. A delete line for src/, docs/, a client folder, or the whole destination means the command is not ready.
Add exclusions before you delete destination files
For developer folders, deletion should happen after filtering. Otherwise rsync spends time comparing disposable folders and may preserve or remove the wrong noise. A clean project backup usually keeps source files, tests, docs, configuration, scripts, migrations, small assets, and lockfiles. It skips dependency folders, generated output, caches, logs, coverage, and machine-local virtual environments.
Create a reusable exclude file:
cat > ~/.rsync-dev-delete-excludes <<'EOF'
node_modules/
.pnpm-store/
.yarn/cache/
.next/cache/
.nuxt/
.turbo/
.vite/
dist/
build/
coverage/
.git/
.venv/
venv/
__pycache__/
.pytest_cache/
.mypy_cache/
vendor/bundle/
tmp/
log/
target/
.gradle/
.DS_Store
EOF
Then use the same file in the dry run and the real run:
rsync -avnih --delete \
--exclude-from "$HOME/.rsync-dev-delete-excludes" \
~/Developer/my-app/ \
/Volumes/DevBackup/my-app/
One subtle point: excluded files are normally protected from deletion too. If node_modules/ exists at the destination and you exclude node_modules/, rsync will not descend into that folder and will generally leave it alone. That can be good if you want local-only destination caches untouched. It can be surprising if you expected the mirror to clean old generated folders that were copied during earlier runs.
If the destination already contains junk from old unfiltered backups, do one deliberate cleanup pass. Either remove those folders manually after verifying the destination path, or use a separate reviewed command for those generated directories. Do not add advanced delete-excluded behavior casually. The readable, safer habit is: filter clean going forward, and clean historical junk explicitly.
Avoid the four rsync --delete mistakes on Mac
1. Reversed source and destination
rsync --delete makes the second path match the first path. If you reverse them, the wrong folder becomes the source of truth. The dry run should show files moving from your working project toward the backup, not the other way around.
2. Missing or wrong trailing slash
~/Developer/my-app/ copies the contents of my-app. ~/Developer/my-app copies the folder itself into the destination. Both can be correct, but the preview should not show accidental my-app/my-app/ nesting.
3. External drive not mounted
Check /Volumes/DevBackup before running the real command. If the drive is missing, a script may write into a local folder named like the drive, and the next run can compare against the wrong tree.
4. Broad excludes hiding real source files
An exclude such as .* may skip important files like .gitignore, .editorconfig, .env.example, or tool configuration. Prefer explicit cache and metadata rules over broad dotfile deletion logic.
When to use --delete-delay, --delete-after, or backups
The default --delete behavior is usually enough for a local developer mirror after a dry run. If you want the log to be easier to inspect, consider --delete-after. It delays deletions until after transfers. If a run fails mid-transfer, that ordering can be easier to reason about.
rsync -avnih --delete-after \
--exclude-from "$HOME/.rsync-dev-delete-excludes" \
~/Developer/my-app/ \
/Volumes/DevBackup/my-app/
--delete-delay is another option that computes deletes during the scan and applies them near the end. For most local Mac backups, the operational habit matters more than the exact delete mode: dry run, read deletes, run real command, and verify a restore occasionally.
If the destination contains work you might need to recover, do not rely on a mirror as your only copy. Use Git remotes for history, Time Machine for whole-Mac recovery, and a separate archive or snapshot when you need point-in-time retention. A mirror is current by design; it is not an archive of every old state.
Where LSyncer fits for delete-based sync
A careful rsync setup is a good fit if you want command-line control, SSH support, and scripts you can keep in a dotfiles repo. The maintenance cost appears when you have several project folders, external drives that are not always mounted, repeated exclude lists, and no visible reminder that the last backup failed.
LSyncer is built for the local Mac developer version of this workflow. You choose folder pairs, keep generated folders such as node_modules, .git, virtual environments, build output, and caches out of the sync, and run jobs manually or on a schedule with visible status. It is a one-time $19.99 Mac App Store purchase, not a subscription.
Use rsync when you need exact flags and terminal-first review. Use LSyncer when the job is a recurring local folder sync and you want exclusions, scheduling, and status without maintaining another shell script.
Best practices for rsync --delete on Mac
- Run
-avnih --deletebefore the first real delete-based sync and whenever paths or filters change. - Search dry-run output for
deletingand explain each destination removal before proceeding. - Keep active projects under a local path such as
~/Developeror~/Code, then sync a filtered copy outward. - Use an explicit
--exclude-fromfile for dependency folders, caches, build output, logs, and macOS metadata. - Keep lockfiles and project manifests. They are the restore contract for rebuilding skipped dependencies.
- Verify external drives and NAS mounts before running real commands under
/Volumes/. - Do an occasional restore test in a scratch folder with
npm ci,pnpm install --frozen-lockfile,pip install -r requirements.txt,bundle install, or your stack's equivalent.
Related reading
- Rsync Dry Run on Mac — preview copy and delete behavior before changing a destination.
- Mirror Folder Mac — decide when a destination should exactly match a filtered project source.
- rsync exclude-from Mac — keep reusable developer exclude files instead of rewriting long commands.
- One Way Folder Sync Mac — use source-to-destination sync without two-way conflict churn.
FAQ
How do I make rsync delete files not in the source on Mac?
Use rsync -av --delete source/ destination/. For safety, preview first with rsync -avnih --delete source/ destination/ and review every deleting line before removing n from the flags.
Does rsync --delete delete files from the source?
No. In the normal source-to-destination form, --delete removes files from the destination when they do not exist in the source. If you reverse the paths, the wrong folder becomes the destination, which is why a dry run matters.
Should I use --delete for Mac project backups?
Use --delete when the destination should be a current mirror of the source after exclusions. Do not use it for archival backups where old versions should be retained. For archives, use snapshots, Time Machine, Git history, or a destination that preserves versions.
Will excluded folders be deleted at the destination?
Usually excluded folders are protected because rsync does not process them. If old generated folders already exist at the destination, clean them deliberately after verifying the path, or use a carefully reviewed separate cleanup command. Do not add advanced delete-excluded behavior unless you understand the effect.
What is the safest rsync delete command for node_modules projects?
Use an exclude file and an itemized dry run: rsync -avnih --delete --exclude-from "$HOME/.rsync-dev-delete-excludes" ~/Developer/my-app/ /Volumes/DevBackup/my-app/. Confirm node_modules/, virtual environments, caches, and build output are not part of the transfer before the real run.