Run df -h and then du -sh ~ on the same Mac and you will often get two numbers that do not add up. df might say your disk has 40 GB free while du on your home folder suggests there is far more data than that would allow, or the reverse: du accounts for everything you can see, yet df insists the disk is nearly full. Neither command is wrong. They are answering different questions using different accounting methods, one at the filesystem level and one by walking files one by one. Once you know which question each one answers, the disagreement stops being confusing and starts being useful diagnostic information.
What df actually measures
df -h asks the filesystem itself how much space is used and free on each mounted volume. It does not look at individual files or folders — it reads accounting figures that the filesystem driver maintains internally, the same numbers macOS uses to decide whether a write will succeed. Run it and you get a table: one row per mounted volume, with size, used, available, and percentage columns, all in human-readable units thanks to the -h flag.
This is the ground truth for "how much can I still write to this disk." It reflects everything the kernel considers allocated, whether or not that allocation is visible in the file hierarchy you can browse. That distinction matters more than it sounds.
What du actually measures
du -sh <path> takes a completely different approach. It walks the directory tree starting at <path>, visits every file and subfolder it has permission to read, adds up their sizes, and prints a total for that one path with the -s flag summarising rather than listing every file. It knows nothing about the rest of the disk and nothing about space the filesystem has set aside outside the directory structure.
du is therefore a measurement of "what is inside this folder," not "how much space is used on this volume." Run it against ~/Downloads and you get an honest answer for that folder. Run it against / and you are asking it to walk almost the entire drive, which is slow and will still miss anything it cannot see, for reasons covered next.
Why open-but-deleted files throw du off
On Unix-derived systems including macOS, deleting a file while a program still has it open does not free the space immediately. The directory entry disappears — so du walking the tree never sees the file and never counts it — but the filesystem keeps the underlying data allocated until every process holding it closes the file. df counts that space as used because the kernel knows it is still allocated. du cannot count it because there is no longer a path pointing to it.
This is a common source of "my disk says it's full but I can't find what's using the space." A long-running application, often a browser or a database, holding a large log or cache file open after you deleted it is enough to produce exactly this gap. Restarting or quitting the offending process releases the space and brings the two figures back into line.
Why APFS snapshots make du look wrong
APFS local snapshots, of the kind Time Machine creates automatically, hold onto the data blocks that existed at the moment the snapshot was taken, even after you delete or change files in the live filesystem. df includes that held space in its used figure because the volume genuinely cannot reuse those blocks yet. du, walking only the current, live directory tree, has no way to see a snapshot at all — snapshots are not part of the folder hierarchy du traverses.
The result is that you can delete a large file, watch it disappear from a du total instantly, and see df's free space barely move, because a recent snapshot is still referencing those blocks. This is expected APFS behaviour, not a bug in either command, and it resolves itself as old snapshots age out or get deleted.
Why mounted volumes and network shares complicate the picture
If a path you point du at contains a mount point for another volume — an external drive, a disk image, or a network share mounted inside your home folder — du's behaviour depends on whether it is told to cross filesystem boundaries. Left to its defaults it may either descend into the mounted volume and count its contents as if they belonged to the parent path, or stop at the boundary and skip it entirely, depending on the flags used. Either way, the total you get for the outer path may not mean what you expect: it can include bytes that physically live on a different disk, or it can silently omit an entire mounted volume's worth of data.
df, by contrast, always reports mounted volumes as their own separate rows, each with its own size and usage figures, because that is how the filesystem layer sees them. This is one of the more common reasons a du total on a path looks implausible: it either double-counts a network share or misses a large one entirely.
Which one answers which question
Use df -h when the question is "how much room do I have left to write new data on this volume." It is fast, always accurate for that purpose, and does not require walking any files.
Use du -sh <path> when the question is "what is actually inside this specific folder." It is the right tool for finding what a directory contains, but it is slow on large trees, blind to snapshots and held-open deleted files, and can be misleading across mount boundaries.
Neither command was designed to answer "why is my disk full and what can I delete," which is really a composite question spanning both: how much space is gone (df), and what visible files account for it (du), with the gap between the two often being snapshots or a process still holding a deleted file open.
Conclusion
du and df are not competing tools, and neither one is broken when they disagree — they are reading from different layers of the same filesystem, one from live directory contents and one from kernel-level block accounting. Understanding that gap turns a confusing mismatch into a useful clue: a large df/du gap on / often points to snapshots or an open-deleted file, while a du total that looks wrong on a specific folder often points to a mount point crossing you did not expect.
Terminal commands are the right tool when you already know roughly where to look. When the gap itself is the mystery — when you need to actually see which folders are large, in proportion, without memorising flags — a visual map is faster. That is the gap VolumeLens is built to close: it walks the same directory tree du would, shows nominal versus on-disk size for every folder, and is upfront when a path could not be read rather than silently reporting zero.