Why FreeUpDisk is safe
to run on your Mac

Most Mac cleaners keep a list of what to delete. FreeUpDisk keeps a list of what it is allowed to touch. Everything else on your disk is off limits, including if you ask.

4

folders it may ever touch

7

checks before any file moves

30

days to undo a cleanup

0

ways to override the rules

Everything on this page describes behavior that ships and has been verified on a test Mac.

The short version

Five things, in plain language. For most people this is the whole answer, and the technical section further down is optional reading.

It works from a permission list, not a junk list

Most cleaners keep a list of things they believe are junk and delete what matches. That breaks the moment something important is not on the list. FreeUpDisk does the opposite. It carries a short list of the only places it is allowed to clean, and if a folder is not on that list the answer is no. Today that list has four entries.

There is no "delete anyway"

Your Keychain, your iCloud Drive files, your Mail, your app containers, and the macOS system folders are blocked in code. If anything asks FreeUpDisk to delete one of them, it refuses and tells you why. There is no preference for this, no advanced mode, no support override, and no force flag anywhere in the app.

It will not touch a file that is in use

Before removing anything, FreeUpDisk checks whether a running process has the file open and whether the app that owns it is running right now. Either one and the file is left exactly where it is, then looked at again on the next pass. Nothing is forced.

Most cleanups are reversible for 30 days

Files are only deleted for good when FreeUpDisk can prove they come back on their own, like your Trash or a handful of caches macOS rebuilds automatically. Everything else is moved into Recovery, where it sits for 30 days with a Restore button. Space is reclaimed either way.

If it does not understand a file, it leaves it

This is the governing rule, not a fallback. Cannot tell which app owns a folder? Refuse. Cannot read a file’s date? Treat it as too new. Cannot resolve a path? Reject. Every ambiguous case in the engine resolves toward doing nothing.

What it will never touch

These locations are refused in code, not by policy. If a bug, a bad suggestion, or someone typing a path into a support tool asks FreeUpDisk to delete one of them, it says no and explains which rule stopped it. There is no setting that changes this.

Your Keychain

Every saved password and certificate. ~/Library/Keychains

Your iCloud Drive files

Deleting these propagates to every synced device. ~/Library/CloudDocs

Your app containers

Where sandboxed apps keep their data. ~/Library/Containers

Your Mail data

Never bulk removed, never trimmed. ~/Library/Mail

The macOS system volumes

System, Preboot, Recovery, VM, plus /bin, /sbin, /usr

The system databases

The privacy permissions database, /private/var/db, swap, /private/var/folders

The full table has 16 entries and lives in the app as compiled-in data. It cannot be changed by a preference file, an environment variable, or a remote flag.

The four folders it is allowed to clean

This is the entire list. Not a summary of it, not the highlights: the complete set of places the current version of FreeUpDisk can remove anything from, and what happens to a file in each one.

~/Library/CachesDeleted for good

Vetted caches only: Safari, Xcode, Chrome’s HTTP cache

Documented caches that macOS and the browser rebuild on their own.

~/Library/Caches30 days in Recovery

Any other cache folder FreeUpDisk does not recognize

Space comes back now. Nothing is destroyed, because a folder sitting in the cache directory is not proof its contents regenerate.

~/Library/Logs30 days in Recovery

Contents only, never the folder itself

Logs are small, so recoverability costs almost no reclaimed space.

~/.TrashDeleted for good

Contents only, and only items added more than 30 days ago

You already threw these away, and they already sat for a month.

What that means for a scheduled clean

A clean that runs while nobody is watching can never permanently destroy a file whose return FreeUpDisk has not proven. The worst it can do to something it does not recognize is move it into Recovery, where you can get it back for 30 days.

Twice now, safety reviews made it clean less

The direction a cleaner leans matters more than any single rule. Both of these changes reduced how much FreeUpDisk deletes outright.

Unrecognized caches stopped being deleted

An earlier version treated every folder under ~/Library/Caches as safe to delete outright. A safety review rejected it. Being inside the cache folder does not prove a file comes back. Unrecognized caches now go to Recovery instead, and a cache earns permanent deletion only by being checked individually and verified in a test machine.

Logs stopped being deleted

Until 19 July 2026, ~/Library/Logs was a permanent delete. The old behavior rested on a convention, that everything an app writes under Logs is disposable. A badly behaved third-party app can break that convention on your Mac, and permanent is permanent. Logs moved to Recovery, and a build test now fails the build if any log rule is ever marked permanent again.

What macOS already protects on its own

Worth knowing, because it closes most of the catastrophic risk before any cleaner runs. A modern Mac is considerably harder to break than the reputation cleaners earned a decade ago suggests.

1

Your files and macOS live on separate volumes

Since macOS Catalina, your startup disk holds several volumes. Your files sit on Data. macOS itself sits on System, which is read-only to every process on the Mac, including root.

2

The system volume is sealed and checked at boot

Since Big Sur, every system file has a hash recorded in the filesystem, and the boot loader verifies the seal before the kernel loads. Tampering with system files does not give you a subtly broken Mac. It gives you a Mac that refuses to boot.

3

System Integrity Protection guards specific paths in the kernel

A kernel-level lock on paths like /System, /usr, /Library/Apple, and the privacy permissions database. It holds even against root and can only be switched off from Recovery Mode.

FreeUpDisk never attempts to disable, work around, or reach past any of it. On top of those protections, it re-checks at runtime that its target really does land on the writable part of your disk, and rejects it if not.

The engine has no network

The part of FreeUpDisk that scans your disk and removes files has two dependencies: the bridge to the interface, and the C standard library. There is no HTTP client in it, no encryption stack, no networking library anywhere in its dependency tree. It is not configured to avoid phoning home. It has nothing to phone home with.

Your paths, your filenames, your file sizes, and how you use your Mac stay on your Mac. Product analytics live in the interface layer only, cover aggregate usage, never carry a filename, a path, or file contents, and can be turned off.

On Full Disk Access, honestly

FreeUpDisk is distributed directly, signed with a Developer ID and notarized by Apple, and it runs outside the App Store sandbox with Full Disk Access, because you cannot measure or clean a disk you cannot see. macOS will not let any app grant itself that permission; you grant it in System Settings and you can revoke it there. The answer to "that is a lot of trust" is not a promise. It is the structure on this page: read access is broad, and the ability to change anything is narrowed to four compiled-in folders behind a gate with no override. What is constrained is capability, not intent.

What FreeUpDisk deliberately does not do

Every line here is a recorded decision, not an omission waiting to be filled in later.

  • Never disables or works around System Integrity Protection, and never asks you to.
  • Never deletes kernel extensions or system launch daemons. Obsolete third-party ones are flagged for you at most.
  • Never touches swap or sleepimage. Deleting them frees nothing permanently and risks a kernel panic.
  • Never touches /private/var/folders, the live temporary tree macOS manages itself.
  • Never touches your iCloud Drive files, because those deletions sync to every device. It points you at Optimize Mac Storage in System Settings instead.
  • Never deletes a sandboxed app’s container while the app is installed.
  • Never bulk deletes Mail data.
  • Never deletes the cache or log folder itself, only what is inside it.
  • Never deletes an app’s leftovers because a folder looks orphaned. The owning app has to be resolvably uninstalled, and unresolvable means refused.

Warning: nerdy section ahead

Below here, it gets technical

Syscalls, inode identity, mount boundaries, and the bugs a test machine caught before you could. Nothing below changes the five points above, so skip it freely. It is here for people who do not believe a safety claim without a mechanism.

One door, and no way around it

Exactly one function in the whole system can move or delete a file. It lives in the safety module of the Rust core and is private to it. The scanner, the categorizer, the cleanup passes, the menu bar interface, and the privileged helper all produce candidate operations, which are inert data describing what they would like to happen. None of them can act.

The interface has no file verbs

The boundary between the interface and the core exposes intent and confirmations, not filesystem operations. There is no deletePath() for the interface to call. You cannot route around a gate you have no way to address.

The compiler enforces it

Inside the core, the destructive primitives are private to the safety module. Producing a candidate carries no privilege to enact it. This is the main reason the safety-critical code is one Rust module rather than logic spread across the interface layer.

The gate does not trust its callers

It re-runs every check itself, on every operation, regardless of what the scanner concluded earlier.

The privileged root helper sits outside all of this because it cannot delete files at all, by design. It manages processes and login items. File deletion has exactly one home.

Every candidate passes all of these, in this order

Order matters: the cheap absolute refusals run first. A reject is terminal and comes with an explanation. A skip is temporary and gets retried later. Neither ever escalates into a deletion.

Before check 1

Resolve the real target

Symlinks, .., and firmlinks are resolved down to an actual file on disk, because every check below reasons about the resolved target rather than the path as it was discovered. A path that does not resolve is rejected. A path that does not land on the writable part of your disk is rejected.

  1. 1

    Allowlist match

    Reject

    Is this on the compiled-in list of four? If not, rejected. The default for your entire disk is untouchable.

  2. 2

    Hard block, checked in both directions

    Reject

    Is the target inside, under, or containing a blocked path? Rejected either way, even on an explicit request. Nothing is ever removed in bulk: every child of a folder is sent through the gate as its own operation, so one protected item nested inside an allowed folder is refused individually while its siblings proceed.

  3. 3

    Is anything using it right now?

    Skip

    Two questions, either one enough to stop it. Is a process holding this file open? Is the app that owns this cache running? Skipped rather than rejected, and looked at again on the next sweep.

  4. 4

    Owner cross-reference

    Reject

    Is the app that owns this data still installed? Rejected. Cannot work out who owns it? Also rejected, because refusing beats guessing.

  5. 5

    Folder versus contents

    Reject

    Is this the named folder itself rather than something inside it? Rejected. Cache and log folders are emptied, never removed.

  6. 6

    Confirmation record

    Reject

    Is there a per-item approval on file for this exact path? No approval, no action. The record binds exactly one resolved target, never a set, a wildcard, or a select-all. It is consumed on use so it cannot be replayed against a later operation, and it is tied to the scan that produced it, so a stale record is rejected.

  7. 7

    Terminal action, then one last look

    Act

    Permanent, or a move into Recovery. If it is a permanent delete and the file shares its data with other filenames on disk, it is downgraded to Recovery rather than unlinked.

Why an unattended run cannot destroy anything unproven

Every run carries an execution context: AUTOMATIC for a scheduled or background sweep with nobody watching, or INTERACTIVE for a person looking at one specific item right now.

The context is set by the trusted core from the actual entry point that started the run, never accepted as a claim from the caller. Scheduled and background entry points are wired to AUTOMATIC with no code path that can produce INTERACTIVE. If an operation shows up claiming INTERACTIVE from a background path, the gate rejects the claim and treats it as AUTOMATIC, the safe value.

Permanently deleting anything not provably regenerable then requires all three of:

1

An INTERACTIVE context, meaning a human is reviewing that exact item.

2

A recorded per-item confirmation naming the path, the size, the owning app, and the specific risk, with the destructive button not set as the default.

3

That person having opted into permanent deletion for that item.

Miss any one and the action falls back to Recovery. There is no automatic path and no confirmation-less path to permanently destroying a file whose regeneration has not been proven. That is a property of the control flow, and a build test asserts it.

Four ways a path-based gate can be fooled, and what closes each one

Symlinks and the check-to-use gap

Every check reasons about the resolved real target, not the path as it was discovered. A symlink inside ~/Library/Caches pointing at /System resolves first and is then caught by the hard block. A dangling symlink does not resolve at all, so it is rejected.

Resolving once is not enough, because a path component can be swapped between the check and the syscall. So the gate resolves and re-checks again at execution time, captures the device and inode identity of both the target and its parent folder (unreadable means skip), then opens the parent directory with O_DIRECTORY and O_NOFOLLOW, re-verifies both identities with fstat on the descriptor it is holding, and acts through the *at syscall family: unlinkat to delete, renameatx_np with RENAME_EXCL to move into Recovery.

RENAME_EXCL is load-bearing on the destination side. A rename that would overwrite an existing file fails instead. A redirected destination can never destroy anything. Purely informational work, like size estimates and owner names for the Recovery list, is computed before the re-check, so the re-check sits immediately next to the syscall it protects.

Volumes mounted inside a cleanable folder

A gate that reasons about paths can still be defeated by a filesystem mounted inside an approved folder, a disk image mounted under a cache directory whose contents then get swept along with it. This is not hypothetical. The test harness caught exactly that, emptying a mounted disk image seeded beneath a candidate folder.

The fix sits below the gate, in the delete primitive itself. The walk anchors a reference device id to the parent side and refuses to cross a device boundary. A folder whose device differs from its parent’s is refused outright, any entry on a different device is skipped and reported rather than deleted, every ancestor that still contains it is left standing, and folders are only removed if already empty, never recursively. The operation reports itself as not completed rather than claiming success. Recovery has the mirror-image guard on the purge side, and undeterminable means refused.

The first version of this guard anchored the reference device to the target itself, which was blind to a volume mounted at the target. Every entry then legitimately shared the mounted filesystem’s device, so the walk saw no boundary at all. Anchoring to the parent fixed it, and it is a fair illustration of why a live test harness exists.

Date added, not date modified

The Trash rule only sweeps items older than 30 days. Written against modification time, that filter is quietly wrong, because moving a file to the Trash preserves its modification date. A file last edited two years ago becomes sweep-eligible the instant you throw it away. The harness caught this too, sweeping a decoy that had been trashed seconds earlier.

The rule now reads ATTR_CMN_ADDEDTIME through getattrlist with FSOPT_NOFOLLOW, which is days since the item was added to the Trash. If that value is unreadable or unavailable, the file is treated as too new and skipped.

Hardlinks

Removing one filename does not free storage that other filenames still point at. Rather than chase link counts or hunt down the other names, the gate fails closed: any permanent delete of a file with more than one name on disk is downgraded to Recovery instead. It never hard-deletes a multi-linked file, regenerable or not, and it never touches the other names.

The rules are compiled in, and the build enforces them

No file, preference, environment variable, remote flag, or support tool can add an entry to the allowlist at runtime. The only way to widen what FreeUpDisk may touch is a source change to the compiled table, which has to cite the section of the safety analysis that supports it, is reviewed as a flagged safety diff by a reviewer whose only job is to block unsafe changes, and has to be verified in a throwaway macOS virtual machine, cleaned, rebooted, and confirmed intact, before it ships. A path with no supporting analysis can only land marked PROPOSED, which the gate cannot reach.

Several invariants are not documentation. They are tests that fail the build:

  • The destructive primitives have exactly one caller, the gate. A source scan lints every module in the workspace and fails the build if anything else so much as references them.
  • No allowlist row may carry a blocked-tier path.
  • Every category that is not provably regenerable must default to Recovery. A row declaring permanent deletion of unproven content fails the build.
  • No compiled row may mark logs as vetted, and every unrecognized log row must default to Recovery.
  • The compiled allowlist must be exactly the four rows from the spec, checked row by row: id, scope, category, terminal action, tier, and conditions.
  • The allowlist roots and the hard-block table must be provably disjoint.
  • Recovery’s own storage location is pinned and asserted disjoint from every cleanable folder and every blocked folder.
  • The confirmation record type admits only single-target binding. A set-valued, wildcard, or select-all record is invalid by construction.

There are 188 tests across the core today, and real filesystem mutation sits behind a build flag that is off by default. With it off, the destructive primitives record what they would have done and change nothing on disk. Turning it on was itself a reviewed safety change.

Every cleanup family is proven on a throwaway Mac

Unit tests are not enough for something that deletes files. The loop is: provision a vanilla macOS image, seed decoy files and tripwires, run a real clean, reboot, verify, jump the clock 31 days forward, let Recovery expire and purge, then verify again.

25 July 2026, macOS 26.5 guest

Passed, 0 failures

40 checks after the clean, 47 after the reboot, 43 after the purge. All 8 seeded decoys byte-identical and all 173 files in the protected tree present in every phase. Verified live: the blocked-path tripwires held, symlink and hardlink cases behaved, a Recovery restore survived a reboot, expiry cleared the store, the decoy inside a mounted disk image stayed intact through both the clean and the purge, and a file trashed seconds earlier with an old modification date survived the first sweep and was only swept after the 31-day jump.

12 July 2026

Failed, five findings

It found the mounted-volume bug, the modification-date bug, two cache rules that were skipping everything, one rule that was unreachable dead code, and two log paths that could not be cleaned without privileges the app deliberately never has. All five were fixed and re-verified. The harness exists because it finds things, and it found things.

One run is still outstanding as a final belt-and-braces check before public distribution: the same suite with System Integrity Protection left fully on. Verification is thorough, and it is not finished.

Known limits

A safety page that claims perfection reads as marketing. These are the sharp edges that are known, documented in the code, and accepted for now.

Detecting whether a file is open has limits

An unprivileged process cannot enumerate another user’s open files. For the two cache rules, "cannot determine" therefore does not mean skip, because it would mean skipping nearly every cache and the feature would do nothing (an early run skipped 91 of 95 candidates for exactly this reason). The working protection for active caches is the running-owner check, scoped to your own user, plus open-file sightings from your own user, which are fully visible. The remaining risk is confined to regenerable caches, where the worst case is a cache removed while a process has it open. macOS keeps open files valid after removal, and the cache rebuilds.

Two microscopic timing windows remain

Both are documented at the call sites. The identity capture is itself path-based, and a single-file unlinkat has an unavoidable gap between check and unlink. macOS offers no equivalent of Linux’s RESOLVE_BENEATH to close them. Both are bounded to acting on an impostor at the exact path the gate had already approved, never anything outside the verified parent folder. Holding the parent descriptor from re-check all the way through is queued as further hardening.

A Recovery move can strand bytes instead of losing them

If Recovery’s own folder were swapped for a symlink by something with write access to your Application Support folder, the bytes land intact outside the store, where both restore and purge refuse to touch them. Fail-safe, but not recoverable from inside the app.

System-wide log folders are not cleaned

/Library/Logs and /private/var/log are safe to clean on paper, but on current macOS they are owned by root, and FreeUpDisk’s privileged helper is built so it cannot delete files at all. Rather than attempt deletions that would fail and then write misleading records, the rules were removed. macOS rotates those logs itself.

The blocked list needs a review every macOS release

Blocked paths on the Data volume, your Keychain, iCloud files, containers, Mail, and the system databases, have no volume-level backstop. The compiled table is their only defense. If a macOS release renames one and the table is not updated, that path quietly loses protection, which is why the per-release review is treated as load-bearing rather than housekeeping.

And what is designed but not shipped yet

Everything described above ships in the current build. These are specified and not yet in it, so nothing on this page depends on them, and none of them is on the four-folder list:

  • Deep Scan, the second pass that reasons about the harder files
  • Performance mode and the privileged helper
  • Developer build artifacts and package caches
  • Leftovers from uninstalled apps, duplicate detection, old iOS backups

When they arrive, they arrive inside the same envelope. A suggestion has no more deletion power than you ticking a checkbox: it still has to clear every check in the gate afterward.

Safety questions people actually ask

Is FreeUpDisk safe to use?

Yes, and the reason is structural rather than a promise. FreeUpDisk can only touch four folders on your Mac: your cache folder, your log folder, and your Trash. Everything else is off limits by default, and a short list of critical locations including your Keychain, your iCloud files, your Mail, and your app containers is refused in code with no way to override it. Every file that is removed passes seven checks, and most are moved into Recovery for 30 days rather than deleted.

Can FreeUpDisk delete something I needed?

It can move something you wanted into Recovery, where you have 30 days to click Restore. Permanent deletion is reserved for two things: your Trash, filtered to items you threw away more than a month ago, and a small vetted set of caches that macOS and browsers rebuild on their own. Anything FreeUpDisk does not recognize goes to Recovery, so a scheduled clean can never permanently destroy a file whose regeneration has not been proven.

Can I make FreeUpDisk clean more aggressively?

No, and that is deliberate. There is no advanced mode, no force flag, and no support tool that widens what the app may touch. The list of allowed folders is compiled into the app, so the only way to add to it is a reviewed source change that has to cite the safety analysis it rests on and pass verification in a throwaway macOS virtual machine before it ships. A safety layer you can switch off is a preference, not a safety layer.

Does FreeUpDisk send my files or filenames anywhere?

No. The engine that scans your disk and removes files has no networking code in it at all. Not disabled, not configured off: there is no HTTP client and no networking library anywhere in its dependencies, so it has nothing to send anything with. Product analytics live in the app shell, cover aggregate usage only, never include a filename, path, or file content, and can be switched off.

Why does FreeUpDisk need Full Disk Access?

Because you cannot measure or clean a disk you cannot see. macOS requires you to grant that permission yourself in System Settings, and no app can grant it to itself. The honest framing is that FreeUpDisk reads broadly and writes to four compiled-in folders behind a seven-check gate with no override. Read access is wide. The ability to change anything is deliberately narrow.

Does FreeUpDisk touch system files or turn off macOS protections?

Never. It does not disable System Integrity Protection, does not ask you to, installs no kernel extensions, and changes no boot arguments. On top of what macOS already blocks, it re-checks at runtime that its target actually lands on the writable part of your disk and refuses it if not.

What happens if FreeUpDisk is not sure about a file?

It leaves it alone. That is the top-level rule of the engine rather than a fallback behavior. If it cannot tell which app owns a folder, it refuses. If it cannot read the date a file was added, it treats it as too new. If a path does not resolve, it rejects it. Unknown means untouched.

Four folders. Seven checks. No override.

Scan your whole Mac, see every gigabyte as a map, and clear what you do not need.
Free to scan on macOS 12 and later.

Free Up My Disk