Diagnostic Playbook — Finding Hidden Space on macOS

When diskutil says X GB used but du can only find Y GB, follow this sequence. The steps are ordered by likelihood of finding the problem — deleted-open files before snapshots, because they’re more common and harder to detect.

The three classes of invisible space

ClassWhat it isTool to detect
Deleted-But-Open FilesProcess holds a file descriptor to a deleted file; blocks not freed until process exitssudo lsof +L1
APFS - SnapshotsSnapshot pins old block versions; space invisible to directory traversaldiskutil apfs listSnapshots
Firmlink traversal failuredu cannot descend from the Data volume rootEnumerate directories individually

DaisyDisk, Finder, du, and find cannot detect any of these. They only see live files with directory entries.

Step 1: establish ground truth

diskutil apfs list

Note the Capacity Consumed for each volume and Container Free Space. The Data volume (role: Data) is almost always where the missing space is.

For a single volume’s authoritative used space:

diskutil info disk3s5 | grep "Volume Used"

Replace disk3s5 with your Data volume identifier from the listing.

Step 2: check deleted-open files FIRST

This is the single most common cause of large invisible disk usage and the one most tools (including DaisyDisk) cannot detect.

# Quick total (upper bound — double-counts shared file descriptors)
sudo lsof +L1 2>/dev/null | awk 'NR>1{sum+=$7} END{printf "%.1f GB in deleted-open files\n", sum/1073741824}'
 
# Show the biggest offenders
sudo lsof +L1 2>/dev/null | awk 'NR>1 && $7+0 > 100000000 {printf "%10.1f GB  PID=%-8s %s  %s\n", $7/1073741824, $2, $1, $9}' | sort -rn | head -30

lsof double-counts shared descriptors

If multiple processes share the same open file descriptor, lsof reports the file size once per process. Actual consumption can be much less than the sum. See Deleted-But-Open Files for details.

If this is large, kill or restart the offending process:

sudo kill <PID>
# Or for launchd-managed services:
sudo launchctl kickstart -k system/<service.label>

Blocks are freed instantly.

Step 3: check snapshots

# Get device identifiers from Step 1, then:
diskutil apfs listSnapshots disk3s5    # Data volume
diskutil apfs listSnapshots disk3s1    # System volume
 
# Also check via Time Machine (can show snapshots diskutil misses)
tmutil listlocalsnapshots /
tmutil listlocalsnapshotdates /

Look for:

  • Non-Apple, non-TM snapshots (com.bombich.ccc.*, com.arq.*) — these are orphaned
  • com.apple.os.update-* on System volume — this is the SSV, expected, ~15 GB
  • MSUPrepareUpdate snapshots — staged but possibly incomplete macOS updates

du cannot traverse /System/Volumes/Data/ from its root due to firmlinks. Enumerate each top-level directory individually:

sudo du -sh /System/Volumes/Data/Applications/ \
           /System/Volumes/Data/Library/ \
           /System/Volumes/Data/Users/ \
           /System/Volumes/Data/private/ \
           /System/Volumes/Data/opt/ \
           /System/Volumes/Data/usr/ \
           /System/Volumes/Data/System/ \
           /System/Volumes/Data/.DocumentRevisions-V100/ \
           /System/Volumes/Data/.Spotlight-V100/ \
           /System/Volumes/Data/.fseventsd/ \
           /System/Volumes/Data/MobileSoftwareUpdate/ \
           /System/Volumes/Data/cores/ 2>/dev/null

Then drill into the largest:

sudo du -hd1 /System/Volumes/Data/Users/yourusername/ 2>/dev/null | sort -rh | head -20
sudo du -hd1 /System/Volumes/Data/Library/ 2>/dev/null | sort -rh | head -20

Step 5: check specific large consumers

# Docker (sparse file — use both ls and du)
ls -lh ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw 2>/dev/null
du -sh ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw 2>/dev/null
 
# Nix store (separate APFS volume — invisible to Data volume scans)
diskutil apfs list | grep -i nix
du -sh /nix/store/ 2>/dev/null
 
# VM volume (swap + sleep image)
ls -lh /private/var/vm/
 
# Corporate tools
sudo du -sh /Library/Application\ Support/*/ 2>/dev/null | sort -rh | head -20
 
# Xcode
sudo du -sh ~/Library/Developer/ 2>/dev/null

Step 6: reconcile

Add up all sources from Steps 2–5. The total should match diskutil info Volume Used Space within a few GB (the remainder is filesystem metadata and FileVault overhead).

If the numbers still don’t add up, you have one of:

  • Deleted-open files you missed (re-run lsof +L1 — processes may have spawned since Step 2)
  • APFS clone extent sharing making du overcount (see APFS - Copy-on-Write and Clones)
  • FileVault encryption overhead (typically small, but can be several GB)

Commands that do NOT work (and why)

CommandWhy it fails on macOS
du -hd1 /System/Volumes/Data/Firmlinks block traversal at root
du -hxd2 /System/Volumes/Data/Same — -x doesn’t help with firmlinks
df -h /Shows container-level info, not per-volume breakdown
diskutil info / → “Purgeable Space”Field doesn’t exist on all macOS versions/volume types
DaisyDisk / Finder “About This Mac”Cannot see deleted-open files (no directory entries)

See also