Who this is for: mobile and cross-platform engineers who run Xcode builds on a remote Mac, plus CI/CD maintainers who care about node stability, reproducible artifacts, and knobs you can actually commit to YAML. Core terms we optimize around: remote Mac, Xcode build cache, Derived Data, iOS CI, and build acceleration. This page is a decision matrix with a hit-condition table, pre-warm checklist, clean-build trade-offs, and disk thresholds with executable parameters. For repo and dependency pull tuning, start from the MacPull technical blog and the Mac CI/CD acceleration guide (Git clone & CocoaPods); for checkout edge cases, see Git Submodule & LFS CI matrix.

Xcode build cache: principles and hit-condition reference table

On Apple platforms, most of what people call “Xcode build cache” is really a bundle of stores under Derived Data: Swift incremental state, Clang module caches, PCH artifacts, index data, and per-workspace SourcePackages when you use Swift Package Manager. A cache hit means the compiler can skip work because inputs (source, flags, SDK, module maps) match a previous fingerprint. On a remote Mac shared by many jobs, misses often come from path churn, Xcode upgrades, switching -derivedDataPath, or cleaning intermediates while leaving metadata inconsistent.

Use the table below as a quick diagnostic: if your symptom matches the left column, the usual miss reason and stabilizing parameter are on the right.

Symptom in iOS CI Likely miss reason Stabilizing parameter or practice
Full recompile after every job Ephemeral DerivedData path or random temp dir Fixed -derivedDataPath per project slug + stable checkout path
SPM re-resolves every run Networked resolve or deleted SourcePackages Persist Derived Data folder; use -clonedSourcePackagesDirPath + commit Package.resolved
“Module not found” after cache restore Mixing Xcode versions or SDK paths Pin DEVELOPER_DIR; invalidate cache on Xcode bump
Slow link phase but fast compile Cold LTO or bitcode pipeline (if enabled) Disable unused modes in CI schemes; keep Debug for PR validation
High CPU in “Indexing” Index store enabled for CI builds COMPILER_INDEX_STORE_ENABLE=NO for pure CI compile jobs

Copy-paste: isolate Derived Data per repo on the runner

export DEVELOPER_DIR="/Applications/Xcode_16.2.app/Contents/Developer"
export DERIVED_DATA_CI="$HOME/ci-derived-data/${CI_PROJECT_NAME:-myapp}"
mkdir -p "$DERIVED_DATA_CI"
xcodebuild -scheme "MyApp" \
  -destination 'platform=iOS Simulator,name=iPhone 16' \
  -derivedDataPath "$DERIVED_DATA_CI" \
  -clonedSourcePackagesDirPath "$DERIVED_DATA_CI/SourcePackages" \
  build

Pair this with a cache key that hashes Package.resolved, Podfile.lock (if CocoaPods), .xcode-version or runner image tag, and the Xcode app path. When any of those change, expect a deliberate miss rather than chasing “flaky cache.”

Remote node pre-warm and cleanup strategy

Pre-warm reduces the first-build penalty after a runner image update or cold disk: run one full xcodebuild of your default scheme against the simulator destination you use in CI, then keep the resulting Derived Data directory on fast local SSD. For teams that share a remote Mac, schedule pre-warm after Xcode installation and whenever the default SDK changes.

Cleanup must be automated; manual deletes do not scale across dozens of branches. Prefer LRU-style eviction of ~/ci-derived-data/* by last-modified time rather than wiping everything on each job, unless compliance requires ephemeral disks.

Phase Goal Executable action
Pre-warm Populate modules & SPM checkouts Nightly xcodebuild build on main with production -destination; persist $DERIVED_DATA_CI
Between jobs Prevent unbounded growth find "$HOME/ci-derived-data" -mindepth 1 -maxdepth 1 -type d -mtime +14 -exec rm -rf {} + (tune days)
Simulators Reclaim disk from old runtimes xcrun simctl delete unavailable; delete unused runtimes in Xcode Settings if allowed
SPM Avoid corrupted checkouts On resolve failure after network blip, remove only SourcePackages/checkouts for the failing package, then rebuild

Node stability improves when CPU thermal throttling and disk pressure are rare: keep at least 15–20 GB free on the volume that holds Derived Data and simulators; crossing into single-digit gigabytes free often correlates with mysterious compile timeouts, not compiler bugs.

Compiler cache vs clean build: parameter trade-offs for iOS CI

“Clean build” in the Xcode UI is not the same as a forensic rebuild. Clean Build Folder removes build products for the workspace but may leave other incremental artifacts depending on settings. For CI, express intent with flags and environment variables instead of clicking menus on a headless remote Mac.

Goal CI approach Trade-off
Fast PR feedback Reuse -derivedDataPath; Debug configuration; single simulator destination May hide rare incremental bugs; run a scheduled full clean nightly
Release parity Separate job with Release + CODE_SIGNING_ALLOWED=NO where possible Slower; still not identical to App Store archive without signing assets
True cold build New empty -derivedDataPath directory each run Longest wall time; use only for mainline or nightly
Skip indexing overhead COMPILER_INDEX_STORE_ENABLE=NO Faster CPU; IDE features irrelevant on CI
# Example: one-line cold vs warm toggle (set CLEAN_CI=1 for cold)
DERIVED_ROOT="${HOME}/ci-derived-data/${CI_PROJECT_NAME:-myapp}"
if [ "${CLEAN_CI:-0}" = "1" ]; then rm -rf "$DERIVED_ROOT"; fi
mkdir -p "$DERIVED_ROOT"
xcodebuild -scheme "MyApp" -destination 'platform=iOS Simulator,name=iPhone 16' \
  -derivedDataPath "$DERIVED_ROOT" build \
  COMPILER_INDEX_STORE_ENABLE=NO

If you need deterministic Swift packages, prefer checking in Package.resolved and passing -disableAutomaticPackageResolution only when you have pre-populated SourcePackages; otherwise resolution errors become hard failures—often desirable on release branches.

Failure fallback and disk water-level threshold FAQ

Use explicit thresholds so operators and scripts agree when to sacrifice build acceleration for node stability. The numbers below are starting points; graph disk use on your own remote Mac fleet and tighten if you see latency spikes near full disks.

What should CI do when xcodebuild fails with “no space left on device”?

Stop the job immediately, alert, then run ordered cleanup: (1) df -h /; (2) prune oldest ci-derived-data trees; (3) xcrun simctl delete unavailable; (4) optional rm -rf ~/Library/Developer/Xcode/Archives/* if archives are not needed on runners. Re-queue the job only after free space is back above your safe floor (for example ≥15% or ≥20 GB, whichever is larger).

When is partial Derived Data delete better than full wipe?

If only one target misbehaves after a merge, delete the subdirectory named after your workspace hash under Derived Data, or remove Build/Intermediates.noindex for that project. Full rm -rf ~/Library/Developer/Xcode/DerivedData/* fixes mystery states but forces a long warm-up for every concurrent job—bad on shared iOS CI nodes.

Recommended disk water-level policy (copy into runbooks)

≥20% free: normal operation.
15–20% free: warning webhook; schedule cleanup after business hours.
10–15% free: aggressive LRU eviction of Derived Data folders older than N days.
<10% free: block new builds; run emergency cleanup; investigate logs for runaway cores or simulator data.

# Disk check snippet for pipeline pre-step (exit 1 blocks the build)
PCT=$(df -P / | awk 'NR==2 {gsub(/%/,"",$5); print $5}')
if [ "$PCT" -ge 90 ]; then echo "Disk use ${PCT}% — refuse build"; exit 1; fi
if [ "$PCT" -ge 85 ]; then echo "WARN: disk use ${PCT}% — trigger cleanup"; fi

Summary and next steps

Summary: Treat Derived Data as part of your iOS CI contract: stable -derivedDataPath, pinned DEVELOPER_DIR, and cache keys that include SPM/CocoaPods lockfiles and image version explain most Xcode build cache hits. Pre-warm after toolchain changes; evict with age-based rules; use COMPILER_INDEX_STORE_ENABLE=NO and deliberate cold builds only where release hygiene demands it. Disk policy (warn 85%, hard stop near 90%, maintain 15–20 GB headroom) keeps remote Mac runners predictable.

Purchase path (no login to browse): compare plans on MacPull pricing, rent hardware from Mac Mini M4 remote Mac purchase, read MacPull help & getting started, or return to the MacPull homepage. Suggested anchor text for editors: “Remote Mac pricing”, “Rent Mac Mini M4 for iOS CI”, “MacPull help center”, “MacPull home”.

More reading: all technical blog articles, Git, npm & CI cache strategy.

Run This Matrix on a Dedicated Remote Mac

Stable Derived Data and fast incremental Xcode builds need SSD headroom and a predictable toolchain. Mac Mini M4 remote Mac with SSH—view pricing and purchase without logging in.