Loosely Decoupled
GuidePython

What's Taking Up All the Space on My Developer Mac?

Find which developer tool is filling your Mac’s disk, such as uv, Xcode or Docker, and which cleanup applies.

A stipple engraving of a tired man with headphones round his neck weighing strongboxes on a brass scale, while a far larger crate with a cyan seal waits unnoticed behind him.
On this page

TL;DR

Record the free space, then narrow down one level at a time with du until a directory belongs to one tool. That tells you which cleanup you need. Clean it with that tool’s own command where it has one, never with rm on a cache a tool manages, and record the free space again; the cleanup’s own report is not what the Mac got back.

Command
$ diskutil info / | grep "Container Free Space"
$ du -hd 1 ~ 2>/dev/null | sort -hr | head -15

Tested with

macOS
27.0.1, MacBook Pro (Apple silicon)
Date
October 2026

My Mac had 37.7 GB free on a 1 TB disk. I expected Xcode to be the reason, since I keep a full set of iOS simulators, and Xcode did hold 79 GiB. The largest directory was a 132 GiB uv cache, which I had never thought about: most of the entries in it were environments uvx had built for MCP servers, covered in part 2. Which directory is largest on your Mac depends on what you build and which tools you run. The method for finding it is below, and the series covers each cause.

Record the free space before you start

On a modern Mac, df -h / reports the sealed system volume, so its Used column is misleading:

Command
$ df -h /

Output

Filesystem        Size    Used   Avail Capacity iused ifree %iused  Mounted on
/dev/disk3s1s1   926Gi    13Gi    40Gi    24%    484k  423M    0%   /

/ is the sealed system snapshot. Your files live on the Data volume in the same APFS container. Every volume in a container draws on one pool of free space, so Used for / counts the system alone, and Avail is the pool. The container’s own figure comes from the startup disk’s container only, so an external APFS drive cannot be mistaken for it:

Command
$ diskutil info / | grep "Container Free Space"

Output

   Container Free Space:      65.8 GB (65847853056 Bytes) (exactly 128609088 512-Byte-Units)

This reading was taken after the cleanup, so it does not match the df output above, which was taken partway through, or the 37.7 GB before any cleanup. df -h prints GiB and diskutil prints GB, so the same moment looks different in the two units. Use one of them for every reading, or the difference between the units looks like progress. A Mac keeps writing while you work, so treat a change of a gigabyte or two as noise.

Find what’s taking up space with du

du reads sizes and changes nothing. 2>/dev/null hides the permission errors that protected directories print.

  1. List the largest directories in your home directory:

    Command
    $ du -hd 1 ~ 2>/dev/null | sort -hr | head -15
  2. Skip the first line, which is the total for the directory you ran it on. Run the same command on the largest directory below it, and repeat until the directory belongs to one tool. On this Mac, that took three levels: ~/.cache, then ~/.cache/uv, then ~/.cache/uv/archive-v0.

  3. Check the two places that hide the most, even if they didn’t top the first list:

    Command
    $ du -hd 1 ~/Library/Caches 2>/dev/null | sort -hr | head -15
    $ du -hd 1 ~/Library/Developer 2>/dev/null | sort -hr
  4. For a package manager, ask the tool where its cache is rather than assuming a path. pip on macOS uses ~/Library/Caches/pip, not ~/.cache/pip:

    Command
    $ uv cache dir

    Output

    /Users/<user>/.cache/uv
    Command
    $ python3 -m pip cache dir

    Output

    /Users/<user>/Library/Caches/pip

A du total is an upper bound on what deleting frees. APFS clones share their blocks with the original until one of them changes, and du counts a cloned file at full size wherever it appears.

Match the directory to its cause

These are the directories that were large on this Mac, and the part of this series that covers each one:

Directory and what fills itSize hereCovered in
~/.cache/uv
Environments that uvx builds for MCP servers
132 GiBPart 2
~/Library/Developer/CoreSimulator/Devices
iOS simulators and the apps installed in them
44 GiBPart 5
~/Library/Developer/Xcode/iOS DeviceSupport
Debug symbols for each device and iOS version you’ve connected
35 GiBPart 5
~/Library/Containers/com.docker.docker
Docker Desktop’s VM disk, Docker.raw
32 GiBPart 3
~/.npm/_npx
Packages that npx runs, MCP servers among them
8.9 GiBPart 4
.build, node_modules, .venv in your projects
Build output and dependencies, including projects you no longer work on
variesPart 6
~/Library/Caches/Homebrew
Cached downloads of formulae and casks
1.1 GiBBelow
~/.ollama/logs
Ollama’s server and app logs
947 MBBelow

For the uv cache, read part 2 before you prune: a running coding session holds a lock that makes the prune wait, and the prune reports far more than the disk gets back. The other parts are listed on the series page.

Some directories stay whatever their size. A Docker volume holds an app’s data, not cache. An app’s Application Support directory holds its state.

Clear Homebrew downloads and Ollama logs

Two directories on the list need no part of their own.

brew cleanup -s removes old versions and the cached downloads of anything no longer installed. It keeps the downloads of what is installed now, so on this Mac it freed about 270 MB of the 1.1 GiB:

Command
$ brew cleanup -s

An app can keep its data somewhere else and still write its logs to the internal disk. My Ollama models live on an external SSD, linked into place:

Command
$ ls -ld ~/.ollama/models

Output

lrwxr-xr-x  1 <user>  staff  ... /Users/<user>/.ollama/models -> /Volumes/<drive>/ollama/models

The logs aren’t linked. List them first:

Command
$ ls -lh ~/.ollama/logs

Here they had reached 947 MB in two files, server-1.log and app-1.log. Yours may have other names. Ollama has no cleanup command for them, so quit Ollama and delete the files that list showed, by name:

Command
$ rm ~/.ollama/logs/server-1.log ~/.ollama/logs/app-1.log

Deleting a log leaves the models and the settings as they are.

Ollama wasn’t in Open at Login, but App Background Activity, under System Settings > General > Login Items, allowed it to run in the background. I turned that off. I didn’t check whether background runs are what filled the logs.

Record the free space again

Run the same command you started with:

Command
$ diskutil info / | grep "Container Free Space"

Here it went from 37.7 GB before any cleanup to 65.8 GB after the uv prune. The cleanup commands had reported more than 140 GB between them. A cleanup command reports what it removed from its own storage; only the container’s figure says what the Mac got back.

Do this with a coding agent

Paste this prompt into your own local coding agent. It finds and reports, and it deletes nothing:

Text
Find what is taking up space on this Mac's internal disk. Measure and report
only; delete, prune or clean nothing. Stay in my home directory, and don't
follow links to external drives.

1. Report free space with diskutil info / (Container Free Space) and
   df -h /, and say which units each prints.
2. Run du -hd 1 on my home directory, then on the largest subdirectory it
   lists (not the total line for the directory itself), and keep going down
   until each large directory belongs to one tool. Also check
   ~/Library/Caches and ~/Library/Developer. Ask package managers for their
   cache paths (for example uv cache dir, python3 -m pip cache dir) instead of
   assuming them.
3. For each directory over 1 GB, report its size, which tool owns it, what
   fills it, and the tool's own cleanup command if it has one.
4. Mark anything that is in use or holds work: running processes using the
   directory (lsof), open coding-agent or editor sessions, uncommitted or
   unpushed changes in any git checkout, Docker volumes, app data under
   Application Support. Say what would be lost if each were removed.
5. Note that du counts APFS clones at full size, so a total is an upper bound
   on what deleting frees.

Then stop and wait. I will choose what to clean.

Common failures

Free space barely moved after a cleanup

Docker reports space inside its VM disk, and du and uv cache size count cloned files at full size. Measure the container with diskutil info / before and after; that is the only figure that says what the Mac got back.

With Time Machine on, a local snapshot can keep a deleted file’s space in use. Time Machine keeps hourly snapshots for 24 hours and deletes them as they age or as space is needed, per Apple’s Time Machine guide (opens in a new tab). List them; on this Mac the list was empty:

Command
$ tmutil listlocalsnapshots /

uv cache prune waits, then times out

uv cache prune waits while any other uv process is running, and every uvx server counts. After five minutes by default (UV_LOCK_TIMEOUT) it gives up; the uv cache documentation (opens in a new tab) covers the lock. Quit the coding sessions that started the servers, then run it again. To see what holds the lock:

Command
$ lsof "$(uv cache dir)/.lock"

Part 2 shows how to find which session holds it.

npm cache clean left ~/.npm large

npm cache clean --force empties npm’s package cache, _cacache. The space is in _npx, where npx keeps the packages it runs; part 4 covers it.

New articles, by email.

A short note when something new is published.