A development Mac fills up from four places in a predictable order: Docker, Xcode, simulators and Android. Each one grows without a cap and none of them cleans up after itself. This is the routine we run once a month, in the order that gives the biggest return first.
Step 1: find out where the space is
df -h /
du -sh ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw 2>/dev/null
du -sh ~/Library/Developer/Xcode/DerivedData
du -sh ~/Library/Developer/CoreSimulator
du -sh ~/.android ~/.gradle 2>/dev/null
Four lines, and you know which of the four systems is the problem this month. On our test machine the answer was 41GB of Docker, 23GB of Xcode output and 12GB of simulators, which is exactly the shape of most developer laptops.
Step 2: Docker, the safe part
docker system df
docker system prune -a
This clears stopped containers, unused networks, dangling images and build cache. Add --volumes only if you are certain no named volume holds anything you need, because that flag is the one that removes data rather than cache.
Then check whether the file on disk actually shrank:
du -h ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw
If it did not, and you need the space, resize the disk image in Docker Desktop under Settings, then Resources. That deletes everything inside Docker, so treat it as a last resort and back up volumes first.
Step 3: Xcode output
rm -rf ~/Library/Developer/Xcode/DerivedData/*
rm -rf ~/Library/Developer/Xcode/iOS\ DeviceSupport/*
Both are rebuildable. Quit Xcode first. Expect the next build of each project to be a full build, and expect DeviceSupport to refill the next time you attach a physical device.
Also worth checking are old archives, which are the builds you shipped to the App Store:
du -sh ~/Library/Developer/Xcode/Archives/* | sort -hr | head
Archives are the one folder in that tree you should keep deliberately. Each contains the dSYMs needed to symbolicate crash reports from a released build, so delete the versions you no longer support rather than all of them.
Step 4: simulators and runtimes
xcrun simctl delete unavailable
xcrun simctl list devices | grep -i unavailable
The first command removes devices whose runtime is gone. The second shows you what is left. For runtimes themselves, open Xcode Settings and remove the iOS versions you no longer test against, which is often 10GB or more.
Step 5: Android, if you build for it
du -sh ~/.gradle ~/.android/avd
./gradlew --stop
rm -rf ~/.gradle/caches/transforms-3
Gradle caches and emulator images are the two big offenders. Delete emulators you have not booted in a month from Android Studio's Device Manager rather than by hand, and back up your signing keys before touching anything inside ~/.android.
Steps 2 to 5 exist as checkboxes. A scan returns one screen with Developer, Browser and System totals, and the Developer group shows each source separately, so you can clear Xcode output without touching Docker volumes. The History tab then gives you a dated line per run showing how much came back.
Step 6: confirm the space returned
df -h /
Compare against step 1. If the number did not move as much as the tool claimed, you have hit the sparse file problem described above, and the fix is a disk image resize rather than more pruning.
Two habits keep this low effort: run it on the first working day of the month, and keep the output of df -h somewhere you will see it. A machine that recovers 60GB a month has a growth rate worth watching.
Where the space usually is, before you start
On a Mac used for iOS or cross platform work, one of these is almost always the answer, and the order is predictable:
| Location | Typical hold | Time to reclaim |
|---|---|---|
| Docker.raw | 20GB to 120GB | 5 minutes with a rebuild |
| Xcode DerivedData | 15GB to 80GB | Seconds after quitting Xcode |
| iOS DeviceSupport | 10GB to 40GB | Seconds |
| Simulator devices and runtimes | 5GB to 60GB | 2 minutes |
| Gradle and Android caches | 5GB to 40GB | 1 minute |
| Archives | 5GB to 50GB | Decide carefully, not a cache |
Order matters, and this is the order
Start with the tool that holds the most and costs the least to lose. Docker is usually that tool, because images rebuild from a registry and volumes are often disposable in development. Xcode comes next, because build output is free to delete once the editor is closed. Simulators after that, since they recreate themselves. Archives last, and only after reading the warning below.
The reason to follow an order at all is that free space on APFS is not always instant. Purges, snapshot thinning and background work can take minutes to show up in Finder, so confirm each step rather than assuming it failed.
Confirm the space actually returned
df -h /
Run it before and after each step and write the numbers down. Two things make the result confusing: local Time Machine snapshots can hold deleted data for up to 24 hours, and a build or index rebuild running in the background can consume space while you are clearing it. Give the machine two minutes of quiet between steps.
Turning this into a monthly habit
The cleanup is only useful if it repeats. A workable routine on the first Monday of each month:
docker system df, thendocker builder prune, then a trim or a rebuild if the image is oversized.- Quit Xcode, delete DerivedData, delete old DeviceSupport folders.
xcrun simctl delete unavailable, and remove runtimes you never boot.- Run
./gradlew cleanin the Android projects you are not testing right now, and prune the Gradle cache. df -h /and compare with last month.
Fifteen minutes of work, once a month, and the disk stops being a source of surprises. If the routine is the part you know you will skip, macoptimize runs the scan on demand and shows each group with its real size, so the decision takes a click rather than a checklist.
FAQ
How long does this take?
First run is usually 15 to 30 minutes, most of it spent waiting for Docker to rebuild its disk image and for Xcode to release its files. The monthly version is closer to 10 minutes, and the only slow step is whatever Docker decides to re-pull.
Will my next builds be slower?
The first one will. Xcode does a full build instead of an incremental one, Docker re-pulls images on first use, and Gradle re-downloads dependencies. After that, everything returns to normal speed and the caches rebuild themselves as you work.
Can I automate it?
The safe parts, yes. A launchd agent or a cron entry that runs simctl delete unavailable and prunes Docker weekly is low risk. Automating DerivedData deletion is also reasonable if you can guarantee Xcode is closed. Automating the disk image rebuild is not, because losing images mid work is a bad surprise.
Is it safe to delete Xcode Archives as part of this routine?
Do not treat them as part of the routine. Archives hold shipping builds and the dSYM files used to read crash reports. Move old dSYMs somewhere durable first, then delete the archives they came from, and never remove an archive for a version still live on the store.
What if free space does not change after a cleanup?
Check for local Time Machine snapshots first, then wait two minutes and measure again. APFS can hold deleted blocks for a while and background indexing eats space in the meantime. If the numbers still have not moved, use df rather than Finder, since Finder rounds and caches its numbers.