Baobaobaolin.com
date
entry
037
topic
tooling
rev
—

On macOS, df / says 82% while the APFS container is 99.4% used

df / said 82% while the APFS container had about 2.7 GiB left. The root is a sealed system snapshot, and the percentage's numerator is only its own usage.

The quickest way to check free disk space is df -h / and a glance at the Capacity column. If your working data lives on the same filesystem as /, that works fine.

On the afternoon of October 13, 2026, this Mac showed 82% for df -h / while the APFS container it sits in had about 2.7 GiB left. This note is about what the percentage for df / measures on macOS: two lines of df first, then what / actually is and how Capacity is computed, and finally what other tools report and how I check now.

Two lines of df, same moment

macOS 26.4.1, 2026-10-13 06:37:19 UTC. The main readings below were taken back to back at that moment; earlier readings and calculated thresholds are marked as such:

$ df -h / /System/Volumes/Data
Filesystem        Size    Used   Avail Capacity iused ifree %iused  Mounted on
/dev/disk3s1s1   460Gi    12Gi   2.7Gi    82%    458k   29M    2%   /
/dev/disk3s5     460Gi   429Gi   2.7Gi   100%    6.1M   29M   18%   /System/Volumes/Data

Size and Avail are identical, Used differs by about 417.7 GiB, and Capacity reads 82% on one and 100% on the other (the Data volume's 100% is a rounded-up display; about 2.7 GiB was still available). Earlier the same day (around 06:05 UTC) the / line showed Avail 3.3Gi and Capacity 78%. In about half an hour available space dropped by roughly 0.6 GiB, and the percentage for / rose four percentage points.

/ is a sealed system snapshot

diskutil apfs list disk3 breaks the container down (rearranged from the raw output, some fields omitted):

Container disk3
  Size (Capacity Ceiling):      494384795648 B (494.4 GB)
  Capacity In Use By Volumes:   491455111168 B (491.5 GB) (99.4% used)
  Capacity Not Allocated:       2929684480 B (2.9 GB) (0.6% free)
  System    Capacity Consumed: 12.6 GB   Snapshot Mount Point: /   Snapshot Sealed: Yes
  Preboot   10.8 GB
  Recovery   2.6 GB
  Data     461.1 GB
  VM         4.3 GB

Volumes in the same APFS container share one pool of space; this container's main volumes are System, Preboot, Recovery, Data and VM (plus one mounted at /System/Volumes/Update). What's mounted at / is a sealed snapshot of the system volume (disk3s1s1; mount shows sealed, read-only). The System volume's Capacity Consumed is 12.6 GB, and df shows its Used as 12 GiB. On this machine the home directory and /tmp are both on the Data volume.

Apple's Platform Security guide says macOS 10.15 moved system content to a separate read-only system volume, and macOS 11 added signing on top (the signed system volume), built on APFS snapshots. So under the same boot snapshot, Used for / usually stays put; writes to the other volumes change the shared Avail.

Capacity is Used ÷ (Used + Avail), rounded up

man df explains it under its APFS example: virtual volumes on a single partition share the same size and available space and differ only in space used; the Capacity column is each filesystem's used space as a percentage of used plus available. The two volumes measured here behave that way.

With the numbers from df -k: for / it's 12,258,500 ÷ (12,258,500 + 2,861,024), about 81.08%, and df prints 82%; for the Data volume it's 450,260,592 ÷ (450,260,592 + 2,861,024), about 99.37%, printed as 100%. Both are rounded up to a whole number. The denominator is "this volume's Used plus the shared Avail"; the numerator is just this volume.

So for the ratio on / to reach 90%, Avail has to fall to one ninth of Used, about 1.3 GiB; for 95%, about 0.6 GiB. Because the display rounds up, "shows 90%" arrives a little earlier, once Avail drops below about 1.44 GiB. Suppose a monitoring rule says "alert when Capacity for df / is above 90%": with Used unchanged, it fires only once Avail is below about 1.3 GiB, whereas 10% of this 460 GiB container would be 46 GiB, so it waits until less than a thirtieth of that is left. (I don't run such a rule; this is computed from the formula.)

Other tools report a different number

At the same time, two common APIs returned this for / (GiB, one decimal):

$ python3 -c 'import shutil; u=shutil.disk_usage("/"); print(*(round(x/2**30,1) for x in u))'
460.4 457.7 2.7
$ node -e 'const s=require("fs").statfsSync("/"); console.log((s.bavail*s.bsize/2**30).toFixed(1))'
2.7

On Unix, Python's shutil.disk_usage computes used as (f_blocks − f_bfree) × f_frsize and free as f_bavail × f_frsize. Here used is 457.7 GiB, and 457.7 ÷ 460.4 is about 99.4%, matching the container's utilization at one decimal, not the 12 GiB in df's single-volume column. Node's fs.statfsSync (added in v18.15.0 and v19.6.0) computes available space as bavail × bsize, also 2.7 GiB. In this measurement every tool's available space rounds to 2.7 GiB; they differ in how "used" is defined and the percentage derived from it.

How I check what's left now

On macOS I now check absolute Avail rather than a percentage. The Data volume's percentage isn't the container's utilization either (here both just happen to be near 99.4%); for the container as a whole, use diskutil. The pre-flight check looks like this, and it stops if df fails or returns something that isn't a number:

#!/bin/sh
df_output=$(LC_ALL=C df -k /System/Volumes/Data) || {
  echo "can't read free space on the Data volume" >&2; exit 1; }
avail_kib=$(printf '%s\n' "$df_output" | awk 'NR==2 {print $4}')
case "$avail_kib" in
  ''|*[!0-9]*) echo "df returned a non-integer for free space" >&2; exit 1 ;;
esac
# Stop with less than 5 GiB left (tune to what the job will write)
if [ "$avail_kib" -lt $((5 * 1024 * 1024)) ]; then
  echo "only $((avail_kib / 1024)) MiB left; clean up first" >&2
  exit 1
fi

This is a one-off check before a job starts, not ongoing monitoring. To find what's eating the space, start with each volume's Capacity Consumed in diskutil apfs list, then run du over directories on the Data volume; du can't see space held by snapshots and is affected by blocks shared between APFS clones, so its totals won't necessarily reconcile with the container. Several jobs were running on the machine that day, and their temporary files may account for the drop, but I didn't measure how much each one wrote.

If you remember one thing

On this Mac, the percentage for df / doesn't tell you how full the APFS container is. The 82% in the opening had the system snapshot's 12 GiB as its numerator and those 12 GiB plus the container's shared free space as its denominator, while the container itself was 99.4% used. To see what's left, read Avail; for the container as a whole, use diskutil.

Revision history

  1. First published