Key takeaways
- Track both
df -handdf -ih; inode exhaustion masquerades as “mysterious” write failures while gigabytes still look free. - Partition retention: hot build caches (hours), warm Xcode artifacts (days), cold logs (weeks), with explicit owner per directory tree.
- Docker and colima-style graphs add overlay small files; pair image pruning with host-level inode checks, not only volume size.
- Size runners against plan SSD minus macOS, apps, and headroom; keep ~15–20% free after worst-case weekly growth.

df, log rotation policies, and your CI cache map.1. Where the bytes and inodes hide
Derived Data and SourcePackages dominate interactive and CI Xcode workloads: incremental builds are fast precisely because indexes linger. On shared runners without per-job ephemeral roots, one branch switch can leave gigabytes per workspace. Swift Package Manager and CocoaPods caches add another layer; Pods trees are notorious for huge file counts even when total size looks modest.
Containers duplicate layers and metadata; bind mounts that pull node_modules or test fixtures from the host multiply inode pressure on the underlying APFS volume. Unified logging (log show, DiagnosticReports, archived stdout from launchd jobs) grows quietly until rotation is tuned. Treat each category as a line item in a simple spreadsheet: expected peak GB, peak inode share, and maximum acceptable age.
Runner economics and queue behavior belong in the same narrative as disk—see 2026 Bitrise cloud iOS versus self-hosted cloud Mac runners: private CocoaPods, parallel workflows, per-minute burn versus queue P95—decision matrix and FAQ for how concurrency choices feed back into cache churn.
2. Quota-style alerts before “disk full” pages
Alert on used percentage and on inode percentage separately. APFS rarely hits classic fragmentation cliffs, but services still misbehave near full volumes because snapshots, Time Machine locals, and CI tempdirs compete for the last margin. Add a growth derivative alert: if seven-day rolling growth exceeds your tier’s runway, page early.
Wire the same channel you use for build failures; disk stalls look like flaky tests. Include which filesystem the job touches—container graph versus host /Users—so operators do not prune the wrong layer during an incident.
# Quick host snapshot (run as part of health checks)
df -h /
df -ih /
du -sh ~/Library/Developer/Xcode/DerivedData 2>/dev/null
docker system df 2>/dev/null || true
3. Tiered cleanup ladder
Tier 0—safe hourly: truncate known-huge CI logs, delete failed job workspaces older than N hours, evict container build cache entries with explicit age TTLs.
Tier 1—daily: prune dangling Docker images and stopped containers; clear per-PR Derived Data folders tied to closed merges; rotate unified logs per your retention policy.
Tier 2—weekly: rebuild cold caches from lockfiles after backing up any credentials; compact SPM caches if corruption checks pass; audit bind mounts for accidental host copies of .git or DerivedData.
Tier 3—break-glass: wholesale remove old Xcode data and global caches only with a maintenance window and checksum verification of reproducible builds. Document what you deleted—auditors ask.
Signing and notarization pipelines also accumulate artifacts; align cache policy with Apple Silicon cloud Mac iOS/macOS CI: codesign, Notarization, stapler & keychain boundaries—reproducible pipelines and rejection-code troubleshooting so intermediate export bundles do not become silent disk anchors.
4. Plan storage boundaries and headroom
kvmboot public tiers center on 16 GB unified memory + 256 GB SSD and 24 GB + 512 GB (see plans and specs). Budget macOS, Xcode, Docker Desktop or equivalent, monitoring agents, and two weeks of logs first; assign the remainder to CI caches and per-runner ephemeral stores. If simultaneous jobs peak during release trains, model worst-case overlap—two large iOS archives plus one containerized backend build can spike both RAM and transient disk before pruning runs.
Keep roughly 15–20% free after that worst case. Falling under ~10% invites mysterious timeouts in compilers, test runners, and package managers that are expensive to debug under outage pressure.
5. Symptom–cause–action
| Symptom | Likely root cause | Preferred action |
|---|---|---|
“No space left on device” but df -h shows free GB |
Inode exhaustion or quota on nested volume | Run df -ih; prune many-small-files trees (Pods, overlays, old logs) |
| CI slower after several green builds | Caches grew; APFS metadata hotspots | Time-box Derived Data; prune Docker builder cache; verify bind mounts |
| Disk alerts only on one runner | Per-host drift: manual debug artifacts | Compare du top-level trees; re-image or enforce janitor script |
6. Closing
Disk governance on cloud Mac runners is not a single cron job—it is a capacity model tying Xcode caches, containers, logs, and plan SSD together with inode awareness. Revisit the model when you change Xcode major versions, enable new parallel jobs, or add Dockerized sidecars; each shift moves both byte and file-count curves.
On cloud Mac mini, predictable SSD makes runner policy stick
Apple Silicon unified memory keeps overlapping compiles and lightweight agents from thrashing swap as aggressively as many x86 laptops. macOS gives you mature Unix tooling—df, log, launchd, and native Xcode paths—so disk runbooks read the same on bare metal and in the cloud. Dedicated cloud Macs isolate your inode curve from noisy neighbors compared with oversubscribed shared pools, while M-series idle power stays low for always-on hygiene jobs. Gatekeeper and SIP reduce drive-by malware risk from random unsigned helpers often seen on commodity Windows CI hosts, and total cost of ownership frequently beats assembling one-off Mac minis you still have to rack, patch, and babysit.
If you want stable Apple Silicon runners with clear SSD tiers for CI and remote builds, kvmboot cloud Mac mini M4 is a practical starting point—see plans and pricing so Derived Data, containers, and logs stay inside a budget you can alert on instead of discover at 2 a.m.