DEV Community

Cover image for How Much Room Is on My Disk? Five Answers, 13.5 GiB Apart
Mustafa ERBAY
Mustafa ERBAY

Posted on Originally published at mustafaerbay.com.tr

How Much Room Is on My Disk? Five Answers, 13.5 GiB Apart

Three days ago this laptop's disk filled up completely. About 370 MiB were left on a
460 GiB data volume; even bash started returning ENOSPC. The cause was boring and it was
my own fault — accumulated agent worktrees, 42 GB. I deleted them and work carried on.

Today I asked the same disk a simple question: how much room do you have?

I got five answers. All in the same second, from the same volume. Between the largest and
the smallest there was a 13.5 GiB gap — thirty-seven times the free space I had left
three days ago, hiding inside the measurement method itself.

This post is the record of chasing that gap. I came out with a rule that isn't documented
anywhere but is reproducible, and along the way one of my hypotheses collapsed.

Five Numbers, One Second

30 September 2026, 13:41:41. The machine is an M4 Pro running macOS 26.6.2 (build 25G83).
The volume I queried is /System/Volumes/Data — where my home directory, my projects,
everything lives.

How I asked Bytes GiB
Total capacity 494,384,795,648 460.43
df / statfs f_bavail 56,507,719,680 52.63
diskutil "Capacity Not Allocated" 56,504,872,960 52.62
volumeAvailableCapacity 56,505,139,200 52.62
volumeAvailableCapacityForImportantUsage 59,102,381,248 55.04
volumeAvailableCapacityForOpportunisticUsage 44,609,720,384 41.55

The first three say practically the same thing; the three-megabyte wobble between them is
whatever got written to disk during the seconds I ran the queries back to back. I count
them as a single answer.

That leaves the two interesting ones. One says 2.42 GiB more than the other three,
the other says 11.08 GiB less. Total spread: 13.50 GiB.

Let me stress this: none of them is wrong. All five are correct. They just answer
different questions, while I thought I was asking all of them the same one.

The Extra 2.42 GiB: Space That Doesn't Exist Yet, but Is Promised

Apple's Disk Utility documentation has a category called "purgeable", and the definition is
plain: space macOS can free up when needed by removing files from your computer; you can't
remove those files yourself, macOS removes them as space is required. The same page says
"Available" can cover free space and purgeable space together.

That's where volumeAvailableCapacityForImportantUsage gets its surplus. That number is not
an inventory, it's a promise: "right now I hold 52.62 GiB, but if you genuinely need it,
I'll find another 2.42 GiB."

Will the promise be kept? Probably. But there's no guarantee handed to the app calling this
API. And I can't itemise that 2.42 GiB of purgeable content from outside — diskutil info
never reports purgeable as its own line on this machine, and there are no Time Machine local
snapshots either (tmutil listlocalsnapshots came back empty for both volumes). So the
number is there; its contents are closed to me.

Apple introduced this key in 10.13 and gives developers a clear split: query "important" when
storing things the user asked for or the app needs to function; query "opportunistic" for
predictive work, like pre-downloading an episode the user might watch.

The Missing 11.08 GiB: A Reserve Nobody Mentions

The bigger difference is on the other side. The "opportunistic" question answers 11.08 GiB
lower than the rest. The system simply doesn't show me part of the disk when the work
is non-essential.

On the first measurement I took this for noise. I looked again two minutes later:

13:36:41  available=61,178,499,072  important=63,774,143,680  opportunistic=49,283,027,008
13:41:41  available=56,505,139,200  important=59,102,381,248  opportunistic=44,609,720,384
Enter fullscreen mode Exit fullscreen mode

Free space dropped 4.4 GiB in five minutes (I was busy with lab images at the time). But the
two differences barely moved: the purgeable share went 2,595,644,608 → 2,597,242,048 bytes,
the reserve 11,895,472,064 → 11,895,418,816 bytes. When the absolute numbers roam by
gigabytes and the distance between them moves by fifty kilobytes, that isn't noise. That's
a coefficient.

Once I knew it was a coefficient, the question I couldn't answer came into focus: what
determines the reserve?
The size of the disk, how full it is, or is it a fixed number?

Lab: A Series of Virtual Volumes

You can't settle this with one disk, because you'd have exactly one data point. So I put
together a series of APFS volumes out of sparse disk images and asked each of them the same
four questions. It costs nothing: a sparse image only occupies what you write to it, and when the
measurement is done it's hdiutil detach and rm.

On empty volumes the first result was instructive on its own: important and available
came back exactly equal, difference zero. So a clean volume has no purgeable content, and
that 2.42 GiB of generosity really does come from accumulated content. Apple's documented
definition matches the measurement one to one.

As for the reserve, here was the first table:

Volume (total) Reserve (bytes) Ratio
2,147,442,688 257,654,784 12.00%
5,158,952,960 619,069,440 12.00%
8,380,178,432 1,005,600,768 12.00%
10,527,662,080 1,263,304,704 12.00%
21,265,080,320 1,283,457,024 6.04%
42,739,916,800 1,283,457,024 3.00%
321,912,791,040 1,283,457,024 0.40%

On small volumes it's exactly 12% of the total; past a certain size it sticks at a fixed
number. That constant is exactly 1,283,457,024 bytes — 1224 MiB to the byte.

One question hangs over that: 12% of what? On an empty volume total and available sit so
close together that you can't tell them apart. To separate them I wrote data in stages to a
1.9 GB volume — a size where the 12% arm applies, not the ceiling:

written      available        reserve       reserve/total   reserve/available
     0 MiB   1,932,550,144    232,833,024       12.00%           12.05%
   256 MiB   1,664,114,688    232,833,024       12.00%           13.99%
   512 MiB   1,127,243,776    232,833,024       12.00%           20.66%
Enter fullscreen mode Exit fullscreen mode

The reserve held steady to the byte while its ratio to available climbed. So the figure is
computed against total and isn't sensitive to how full the volume is. I'd tried this on a
40 GB volume in the first round, but that one sits on the ceiling arm, where the test can't
distinguish anything; only the 12% arm could settle it.

From that I wrote a rule:

reserve = min(12% of total capacity, 1,283,457,024 bytes)

If the rule holds, the knee is calculable too: 1,283,457,024 ÷ 0.12 =
10,695,475,200 bytes. Volumes below that size should be on the 12% arm, those above it
at the ceiling.

I Tested the Hypothesis, and It Failed

I took readings on both sides of the knee and the rule didn't hold. From 10.0 GB to 10.5 GB the
reserve didn't budge across five separate sizes; it was 20 MB off my prediction and every
one of them returned the same number to the byte.

It's easy to wave a result like that away as "roughly right". I didn't, because five
separate volumes answering identically to the byte is physically odd. I went back to the
script: on that round I'd given every image the same -volname. macOS mounts the second
volume as /Volumes/N 1; my mount-point parsing kept pointing at the first one. All five
measurements had read the same disk.

There's a lesson in that: a repeated number can be a copy, not a confirmation. Getting
the same answer five times should have made me suspicious, not convinced.

I fixed the script — a unique name per volume, exact sizes in sectors, shuffled order — and
went back to both sides of the knee:

total=10,690,002,944 (below knee)  reserve=1,282,768,896   12% arm
total=10,700,001,280 (above knee)  reserve=1,283,457,024   CEILING
Enter fullscreen mode Exit fullscreen mode

The transition was caught inside a ten-megabyte window, and the threshold I'd calculated
(10,695,475,200) landed almost exactly in the middle of it. Across the wider sweep the
deviation stayed under 44,236 bytes — four parts per million on a 10 GB volume, the cost of
block alignment.

Filling that same volume further exposed one more limit to the rule. Once available drops
below the reserve, opportunistic doesn't go negative — it clamps to zero:

available=352,346,112  ->  opportunistic=119,513,088
available=142,630,912  ->  opportunistic=0
available= 37,773,312  ->  opportunistic=0
Enter fullscreen mode Exit fullscreen mode

So an app asking on behalf of background work is told "no room at all" while 142 MB are still
free on the disk. That is precisely the reserve's job; but someone seeing the number for the
first time could easily read it as a completely full disk.

Diagram

Where the Rule Breaks: the Boot Volume

Now the part where I have to be honest. The rule I derived works on ordinary data volumes.
On this machine's own Data volume it doesn't.

There the reserve is 11,895,418,816 bytes. The rule's 12% arm would say 59.3 GB, its ceiling
arm would say 1.3 GB; the measured value is neither, it's 2.41% of the total. So the boot
volume answers to a third rule.

I don't know what that rule is. I have one machine, one system volume, and not enough data
points to derive a formula from the outside. Apple doesn't document how these numbers are
computed anywhere — all it documents is what the keys are for. So writing a guess here would
be doing exactly what I complain about at the top of this post: presenting a policy as a fact.

What I do know: the reserve is stable there too. Across five minutes it moved fifty
kilobytes. It's definitely a coefficient, just a different value.

A Sixth Misreading: df's Columns Don't Add Up

Something else caught my eye while measuring, and it must mislead people more often than the
APIs do, because everyone runs df. At 13:46:15 I asked about three volumes at once:

Filesystem        Size    Used   Avail Capacity  Mounted on
/dev/disk3s1s1   460Gi    12Gi    49Gi    20%    /
/dev/disk3s5     460Gi   382Gi    49Gi    89%    /System/Volumes/Data
/dev/disk3s6     460Gi   8.0Gi    49Gi    15%    /System/Volumes/VM
Enter fullscreen mode Exit fullscreen mode

Look at all three rows: the Size column is identical, and so is the Avail column. And on
no row does Used plus Avail equal Size — on the first, 12 + 49 = 61 GiB, while Size says
460 GiB.

This isn't a bug. In APFS, volumes inside a single container share free space; Apple
documents this plainly. Since df has to print one row per volume, it writes the container's
ceiling as every volume's "Size" and the container's free space as every volume's "Avail".
That 49 GiB is one pool, reported three times.

On this machine the container carries five volumes, and the real distribution is: Data
410.2 GB, System 12.6 GB, Preboot 9.0 GB, VM 8.6 GB, Recovery 1.3 GB. A total of 441.9 GB in
use, 52.5 GB not allocated.

Two practical consequences. First, if you've written a monitoring script that sums the
"Avail" values across multiple rows, you're counting space that doesn't exist — a 147 GiB
fantasy for three rows. Second, df's percentage column (Capacity) isn't that volume's own
fullness; it's a ratio computed against the shared pool. The 89% for the Data volume is
meaningful, but the 20% shown for / doesn't mean that volume is a fifth full.

And a quiet surprise: the VM volume holds 8.6 GB, and that's swap. I looked inside — it's
full of one-gigabyte swap files; sysctl vm.swapusage says 7,671 MB of a 8,192 MB total are
in use. The machine has 24 GiB of memory. So memory pressure has quietly claimed eight
gigabytes of the disk for itself — the second half of the story that filled my disk three
days ago sits right here, but that's a subject for another post.

So Which Number Should You Watch?

This isn't academic curiosity for me, because three days ago this disk filling up stopped my
work. Here's where I landed for my own use:

For monitoring and alerts, statfs. The number df gives is the barest one: how many
bytes can actually be written right now. It carries no purgeable promise and deducts no
reserve. If you're setting a threshold alarm, this is the one least likely to lie to you.

For "will this file fit", ForImportantUsage. If you're doing something the user asked
for, trusting the system to free purgeable space is reasonable. That's the use Apple
describes anyway.

For background work, ForOpportunisticUsage. For things that can wait — taking backups,
pre-downloading, inflating caches. Don't let the 11 GiB reserve annoy you; that reserve is
what stops a background job from driving your disk to zero at midnight.

And one rule I made for myself: if I don't know which number I'm looking at, I don't build
an alarm on it.
In the disk-full event three days ago, the Lima virtual machine saw "plenty
of room" from the inside, because it couldn't see that its own sparse image had nothing left
backing it on the host. Same family of mistake.

Then there's the thing I stumbled into that surprised me most. On one of the lab rounds I'd
run hdiutil attach with -nobrowse, and the results broke. Since I'd changed two flags at
once, I didn't know which was to blame at first; I tried all three separately. The culprit
is -nobrowse:

no flags     available=12,554,981,376  important=12,554,981,376  opportunistic=11,271,524,352
-mountpoint  (same as the no-flag round: reserve again 1,283,457,024)
-nobrowse    available=12,554,989,568  important=0               opportunistic=0
Enter fullscreen mode Exit fullscreen mode

On a volume that doesn't show up in Finder, both purpose-aware APIs return zero — while
available still reports 12.5 GB of free space. An app following Apple's recommended path
would look at a completely empty disk and conclude there's no room. I couldn't find this
documented anywhere; I'm noting it as behaviour from my own readings.

Not a Number, a Policy

Three days ago I also wrote about putting CI on this machine,
where the bill came due in memory arithmetic. The same thing is happening here, a little
more quietly.

We run our digital lives on a handful of numbers: charge remaining, space remaining, quota
remaining. We take these for measurements — thermometer-like, something that looks at reality
and reads it out. Most of them are reality with a policy laid over it. One is being generous
and counting space you don't have yet; another is being cautious and hiding part of the space
you do have. Both are well-intentioned, both are reasonable; but neither is telling you "your
disk has this much room." They're telling you "I made this decision on your behalf."

The 13.5 GiB spread I found is the size of that decision. And the odd part is that the
system isn't hiding it from me; even the names of the keys state the intent outright — "for
important usage", "for opportunistic usage". Nobody is being deceived. It's just that none of
us reads those names, because when we ask the question we assume there's a single answer.

Ask this of your own setup: if you have a disk alarm, which number feeds it? If you don't
know the answer, that alarm may not be telling you anything.

Official Sources

Top comments (0)