DevOps & Audit April 16, 2026

2026-04-16 iOS CI Disk Cleanup: Simulator Runtimes, Archives & Janitors on Leased Cloud Mac

MacXCode Engineering Team April 16, 2026 ~14 min read

Leased Mac mini M4 builders in Hong Kong, Tokyo, Seoul, Singapore, and US East look infinite until df -h shows the data volume under 12% free—then every xcodebuild test becomes a lottery. This 2026-04-16 guide explains why disks fill even when compile caches are isolated, maps the largest directories, lists copy-paste simctl maintenance, defines an Archives retention policy, ships a launchd janitor template, and points to per-job DerivedData isolation, headless simulator testing, and Simulator runtime disk budget & selective install (2026-05-06) so your hygiene story stays coherent end-to-end.

Why Cloud Mac Disks Fill When “We Already Clean DerivedData”

Per-job DERIVED_DATA_PATH solves compile races but not global consumers: every iOS runtime you install for Xcode 16.x still lives under /Library/Developer/CoreSimulator; every UI test can add 300–900 MB of device data; every Archive duplicates dSYMs and bitcode slices. Teams that rotate three Xcode minors on one host without uninstalling old runtimes routinely lose 80–140 GB before noticing. Pair disk telemetry with queue depth so you scale via pricing only after janitors stop moving the needle.

  • Stale booted simulators keep CoreSimulator mounts alive and block deletion.
  • Old watchOS / tvOS runtimes linger when only iOS targets are built.
  • Archives accumulate because nobody owns retention on shared SSH hosts.
  • xcresult bundles moved to /tmp survive reboots on some images—sweep them explicitly.

Footprint Map: Where the Gigabytes Hide

Path / area Typical range Safe to automate?
~/Library/Developer/Xcode/Archives 15–120 GB cumulative Yes, with age-based policy + object storage upload first
~/Library/Developer/CoreSimulator/Devices 8–60 GB Partial—delete unavailable + prune media caches
/Library/Developer/CoreSimulator/Volumes (runtimes) 25–90 GB multi-version Careful—coordinate with required SDK matrix
~/Library/Developer/Xcode/iOS DeviceSupport 5–25 GB Yes for versions older than 180 days if policy allows
Alerting: page when free space < 50 GB on the NVMe volume that hosts both /Users and /Volumes/builds; hard-stop new jobs when < 25 GB to avoid kernel write failures mid-archive.

simctl Commands Operators Actually Run

Run these only when the CI queue reports zero active simulator jobs, or scope them to a maintenance label that drains runners first.

xcrun simctl shutdown all xcrun simctl delete unavailable xcrun simctl erase all # only on disposable preview hosts—never on shared prod-like builders without approval

To list runtimes and decide what to uninstall from Xcode’s “Platforms” pane or xcodebuild -downloadPlatform workflows, capture xcrun simctl list runtimes weekly into your config repo so Tokyo and Singapore nodes stay aligned.

Archives: Retention Math That Finance Understands

Treat .xcarchive like regulated artifacts: upload to object storage, keep local copies for 14 or 30 days depending on rollback policy, then delete. If legal requires 90-day local retention, buy disk headroom explicitly—do not pretend compression will save you; zip rarely beats 35% on already-compressed slices.

Retention Approx. monthly growth (single app, weekly archive) Mitigation
7 days local 12–20 GB Nightly tar + upload + delete
30 days local 45–70 GB Tiered storage + checksum verification
∞ “just in case” Unbounded Forbidden on shared leased Macs

Janitor Jobs with launchd (No ClickOps)

Wrap maintenance in a non-interactive script owned by the CI user; log to ~/Library/Logs/ci-janitor.log; exit non-zero if any step deletes more than 15 GB in one pass so Slack gets a breadcrumb. Schedule Sunday 03:00 local time per region—Hong Kong Sunday is still Saturday evening in California, so stagger if your team spans zones.

Never delete ~/Library/Developer/Xcode/UserData wholesale—IDE snippets and breakpoints live there; engineers will revolt and you will re-open “mystery red builds” tickets.

Pair With DerivedData & Test Strategy

After global cleanup, ensure each pipeline still sets DERIVED_DATA_PATH per job as in the 2026-04-15 isolation article. For UI-heavy suites, re-read headless simulator testing to cap parallel destinations so janitors do not fight active SimulatorTrampoline processes.

Watchpoints After Aggressive Cleanup

Expect first post-janitor job to spend +4 to +11 minutes re-downloading symbols or rebuilding device support if you trimmed DeviceSupport. Track p95 build time for 48 hours; if it jumps more than 22%, your policy was too aggressive—restore from backup or widen retention.

FAQ: Disk Hygiene on Rented Apple Silicon

Question Short answer
Should we symlink Archives to NFS? Only with low-latency links; otherwise upload-and-delete locally.
Does Apple Silicon compress CoreSimulator data? APFS clones help some templates; do not rely on it for accounting.
Who owns the janitor script? Platform team + on-call rotation documented in help.

Regions, NVMe, and When to Add Another Mac

Disk pressure is often a proxy for too many workloads on one host. If janitors reclaim >40 GB weekly and you still flirt with 20% free, split teams across JP vs SG builders or add a second US East node—latency to Git and ASC matters as much as bytes. Use pricing to compare bare-metal tiers; keep blog index bookmarked for signing, notarization, and OpenClaw runbooks.

Bottom line: global simulator + Archive hygiene is not optional on shared leased Macs—it is the difference between predictable nightlies and “red until someone SSHes in.” Automate it, measure it, and pair it with per-job DerivedData discipline.

Lease M4 builders with room to breathe

HK · JP · KR · SG · US · SSH / VNC