Transfer filters can leave receiver-only caches and temporary files behind because omitted paths are protected from deletion by default. A cleanup mirror can remove those filtered paths, but it must keep any receiver-only data covered by an explicit protection rule.
The --delete-excluded option changes ordinary include and exclude rules into sender-side rules, so matching names no longer receive automatic deletion protection on the receiver. An explicit protect filter still applies to the receiver and provides a deliberate exception for local-only content.
Keep the dry run and live run identical except for --dry-run. The deletion preview must name only planned excluded paths; a repeat dry run with no itemized lines, together with the preserved control file, shows that the receiver converged without crossing the protection boundary.
# /srv/fixture/exclude-rules.txt /cache/ *.tmp
Patterns are relative to the transfer root. The anchored rule matches the root-level cache directory, while *.tmp matches temporary filenames at any depth.
$ find /srv/fixture/destination -mindepth 1 -print /srv/fixture/destination/cache /srv/fixture/destination/cache/session.dat /srv/fixture/destination/app /srv/fixture/destination/app/app.py /srv/fixture/destination/reports /srv/fixture/destination/reports/summary.txt /srv/fixture/destination/reports/draft.tmp /srv/fixture/destination/local-only /srv/fixture/destination/local-only/keep.txt
The local-only/keep.txt file is the receiver-only control. It must remain after the cleanup mirror.
$ rsync --archive --dry-run --itemize-changes --delete --exclude-from=/srv/fixture/exclude-rules.txt --filter='protect /local-only/' /srv/fixture/source/ /srv/fixture/destination/
No itemized lines means the ordinary exclude rules are protecting cache/ and *.tmp on the receiver. The explicit protect rule also shields local-only/.
$ rsync --archive --dry-run --itemize-changes --delete --delete-excluded --exclude-from=/srv/fixture/exclude-rules.txt --filter='protect /local-only/' /srv/fixture/source/ /srv/fixture/destination/ *deleting cache/session.dat *deleting cache/ *deleting reports/draft.tmp
--delete-excluded removes receiver paths that match ordinary exclude rules. Stop if a *deleting line names data that must remain, or if an intended receiver-only path lacks an explicit protect rule.
$ rsync --archive --itemize-changes --delete --delete-excluded --exclude-from=/srv/fixture/exclude-rules.txt --filter='protect /local-only/' /srv/fixture/source/ /srv/fixture/destination/ *deleting cache/session.dat *deleting cache/ *deleting reports/draft.tmp
The live command deletes the reviewed receiver paths. Confirm that the destination is confined to the intended mirror before running it.
$ find /srv/fixture/destination -mindepth 1 -print /srv/fixture/destination/app /srv/fixture/destination/app/app.py /srv/fixture/destination/reports /srv/fixture/destination/reports/summary.txt /srv/fixture/destination/local-only /srv/fixture/destination/local-only/keep.txt
The excluded cache and temporary report are gone, while the receiver-only control remains because of the explicit protect rule.
$ rsync --archive --dry-run --itemize-changes --delete --delete-excluded --exclude-from=/srv/fixture/exclude-rules.txt --filter='protect /local-only/' /srv/fixture/source/ /srv/fixture/destination/
No itemized lines means no in-scope transfer or deletion remains under the same source, destination, exclusion, and receiver-protection rules.