Developer working at Mac computer looking at code on screen

It's 2 a.m. and your CI pipeline fires an alert: build node has less than 2 GB free — xcodebuild exits immediately. You open Terminal, run df -h, and your 256 GB SSD is full. ~/Library/Developer alone holds 180 GB, and a folder called DerivedData accounts for more than 90 GB of it.

This is not a one-off. On an M4 Mac Mini that has run for eight months without cleanup, DerivedData typically accumulates 40–100 GB; add simulator images and device symbol files, and ~/Library/Developer topping 150 GB is common. This article walks you through root cause → fast diagnosis → per-folder cleanup → CI prevention → automated maintenance — a playbook you can copy directly.

By the end you will have: a safety guide for each DerivedData subdirectory, a one-liner to find the biggest disk hog, cleanup scripts for both local and CI, and an automation setup so disk alerts stop coming back.

Quick Answer: three most common questions

Question Direct answer Risk
Can I delete DerivedData? rm -rf ~/Library/Developer/Xcode/DerivedData/* is completely safe Next build is full rebuild — about 2–5 minutes slower
Can I delete Archives? Versions with no active users: yes; keep dSYM for live releases Cannot symbolicate crash logs for deleted versions
CI node disk almost full — quick fix? DerivedData → unavailable simulators → old DeviceSupport, in that order Next build rebuilds indexes — about 3 minutes slower

What DerivedData actually stores

Many developers know "clear DerivedData fixes weird build issues" but not what is inside. That leads to two mistakes: deleting something you should keep, or keeping what should go — and watching the disk fill up again. Here are the most important paths under ~/Library/Developer:

Path Contents Typical size Safe to delete?
Xcode/DerivedData Build artifacts, indexes, build logs, Swift module cache 5–60 GB ✅ Fully safe — rebuilt automatically on next build
Xcode/Archives Release builds (.xcarchive) including dSYM symbol files 2–30 GB ⚠️ Old versions OK; keep dSYM for live releases
Xcode/iOS DeviceSupport Debug symbols downloaded when connecting physical devices 1–3 GB per iOS version ✅ Old versions OK — re-downloaded on reconnect
CoreSimulator/Profiles/Runtimes Simulator runtime images 5–10 GB per iOS version ⚠️ Unused versions OK — must re-download
CoreSimulator/Devices Created simulator instance data 1–15 GB ✅ Unavailable state — safe to delete
Caches/org.swift.swiftpm SPM package cache 1–8 GB ✅ Safe — rebuilt on next resolve
Caches/com.apple.dt.Xcode Xcode internal cache (previews, IB) 0.5–3 GB ✅ Safe
Key takeaway: Everything under DerivedData is rebuildable cache — completely independent of source code, Provisioning Profiles, and App Store submission records. The only cost of deletion is a full rebuild next time (about 2–5 extra minutes for a mid-size project).

DerivedData subdirectory layout

Each project gets its own subdirectory under DerivedData, named {ProjectName}-{random-hash}/. Internal structure:

MyApp-abcdefghijklmn/
├── Build/
│   ├── Products/          # Build outputs (.app, .framework, .o files)
│   └── Intermediates.noindex/  # Intermediate files (.o, .d dependency graph)
├── Index.noindex/         # SourceKit index (jump to definition, autocomplete)
├── Logs/                  # Build logs (xcodebuild -resultBundlePath also lands here)
└── info.plist             # Project path record

Build/Intermediates.noindex is usually the largest — a mid-size project with 200 Swift files can easily exceed 2 GB here; add Index.noindex and a single project's DerivedData folder hitting 5–10 GB is common.

Five steps to find the disk hog fast

Do not start deleting blindly. Spend three minutes diagnosing — it saves time. This flow was tested on a 256 GB M4 Mac Mini and finds the top 5 directories in under two minutes.

Step 1: Check overall disk usage

# View full-disk usage
df -h /

# Quick top 10 directories (entire root volume)
sudo du -sh /* 2>/dev/null | sort -rh | head -10

Step 2: Break down the Developer directory

du -sh ~/Library/Developer/Xcode/* 2>/dev/null | sort -rh
du -sh ~/Library/Developer/CoreSimulator/Profiles/Runtimes/* 2>/dev/null | sort -rh
du -sh ~/Library/Developer/Xcode/iOS\ DeviceSupport/* 2>/dev/null | sort -rh

Sample output (CI node running 8 months):

91G    /Users/ci/Library/Developer/Xcode/DerivedData
28G    /Users/ci/Library/Developer/CoreSimulator
18G    /Users/ci/Library/Developer/Xcode/iOS DeviceSupport
12G    /Users/ci/Library/Developer/Xcode/Archives
 6G    /Users/ci/Library/Developer/Xcode/Caches

Step 3: Find the biggest DerivedData sub-projects

du -sh ~/Library/Developer/Xcode/DerivedData/* 2>/dev/null | sort -rh | head -10
Real numbers: On a dev machine with 15 historical projects, the top 3 DerivedData subdirectories were 18 GB, 12 GB, and 9 GB — two projects had not been opened in over a year, wasting 30 GB.

Step 4: Check simulator usage and status

# List all simulators and their status
xcrun simctl list devices

# Show only "unavailable" (left over after OS upgrades)
xcrun simctl list devices | grep -i "unavailable"

Step 5: Confirm DeviceSupport version distribution

ls -lh ~/Library/Developer/Xcode/iOS\ DeviceSupport/

When output looks like this, you have years of accumulated device symbol files:

drwxr-xr-x  3 dev  staff   96B  iOS 15.4 (19E241)
drwxr-xr-x  3 dev  staff   96B  iOS 16.1 (20B82)
drwxr-xr-x  3 dev  staff   96B  iOS 17.2 (21C62)
drwxr-xr-x  3 dev  staff   96B  iOS 18.3.2 (22D82)

iOS 15 and 16 symbol files have almost no value in 2026 (App Store minimum deployment targets are widely at iOS 17+) — safe to delete.

Per-folder cleanup: safe, cautious, do not touch

Safe to delete (zero risk)

# 1. Clear all project build caches
rm -rf ~/Library/Developer/Xcode/DerivedData/*

# 2. Delete unavailable simulators
xcrun simctl delete unavailable

# 3. Clear SPM package cache
rm -rf ~/Library/Caches/org.swift.swiftpm/*

# 4. Clear Xcode internal cache
rm -rf ~/Library/Caches/com.apple.dt.Xcode/*

# 5. Clear SwiftUI preview cache
rm -rf ~/Library/Developer/Xcode/UserData/Previews/*

Selective deletion (remove old, keep new)

# Delete device symbols from more than 2 versions ago (keep latest two iOS versions)
# First confirm which versions exist
ls ~/Library/Developer/Xcode/iOS\ DeviceSupport/

# Example: keep only iOS 18.x, delete earlier
rm -rf ~/Library/Developer/Xcode/iOS\ DeviceSupport/iOS\ 15*
rm -rf ~/Library/Developer/Xcode/iOS\ DeviceSupport/iOS\ 16*
rm -rf ~/Library/Developer/Xcode/iOS\ DeviceSupport/iOS\ 17*

Handle with care: Archives

  • Confirm the version is off the App Store or has no active users
  • If users remain: export dSYM separately (Organizer → right-click Archive → Show in Finder → copy .dSYM)
  • Upload dSYM to Firebase Crashlytics / Sentry or your crash analytics platform
  • Delete the Archive only after the platform confirms receipt

One-shot emergency commands (disk critical)

Run these in safe order — typically frees 20–60 GB quickly:

#!/bin/bash
# emergency-clean-xcode.sh
# Before running: confirm no xcodebuild processes are active
echo "=== Xcode disk emergency cleanup ==="

echo "[1/4] Cleaning DerivedData..."
du -sh ~/Library/Developer/Xcode/DerivedData 2>/dev/null
rm -rf ~/Library/Developer/Xcode/DerivedData/*
echo "✓ DerivedData cleared"

echo "[2/4] Deleting unavailable simulators..."
xcrun simctl delete unavailable
echo "✓ Unavailable simulators deleted"

echo "[3/4] Cleaning SPM and Xcode caches..."
rm -rf ~/Library/Caches/org.swift.swiftpm/*
rm -rf ~/Library/Caches/com.apple.dt.Xcode/*
echo "✓ Caches cleared"

echo "[4/4] Cleaning preview cache..."
rm -rf ~/Library/Developer/Xcode/UserData/Previews/*
echo "✓ Preview cache cleared"

echo ""
echo "=== Cleanup complete — current disk status ==="
df -h /
💡 Root fix: If your CI node is a local physical machine, disk management is a permanent burden. Macstripe offers dedicated cloud M4 Mac Mini nodes — billed by day, week, or month, reset when done — so DerivedData buildup disappears at the source. View plans & pricing →

CI/CD pipeline anti-blockage strategies

Manual cleanup on a dev machine is manageable; an unattended CI runner needs systematic protection. Below are three approaches for self-hosted macOS runners (GitHub Actions / GitLab CI).

Option A: Fixed DerivedData path + incremental cache

Point DerivedData to a controlled path and reuse across jobs via CI cache:

# .github/workflows/ios-build.yml excerpt
- name: Build
  run: |
    xcodebuild \
      -project MyApp.xcodeproj \
      -scheme MyApp \
      -configuration Release \
      -derivedDataPath /tmp/derived-data \
      -resultBundlePath /tmp/result.xcresult \
      build

- name: Cache DerivedData
  uses: actions/cache@v4
  with:
    path: /tmp/derived-data
    key: derived-data-${{ runner.os }}-${{ hashFiles('**/Package.resolved') }}-${{ hashFiles('**/*.xcodeproj/project.pbxproj') }}
    restore-keys: |
      derived-data-${{ runner.os }}-
Note: Cache keys must include hashes of Package.resolved and project.pbxproj. Otherwise a Swift Package upgrade can hit stale cache and fail the build.

Option B: Auto-clean stale cache before/after builds

# Before build: remove DerivedData subdirectories unused for 7+ days
- name: Clean stale DerivedData
  run: |
    find ~/Library/Developer/Xcode/DerivedData \
      -maxdepth 1 -mindepth 1 \
      -type d \
      -atime +7 \
      -exec rm -rf {} +
    echo "DerivedData cleanup done — current size:"
    du -sh ~/Library/Developer/Xcode/DerivedData 2>/dev/null || echo "Directory empty"

Option C: GitLab CI disk capacity gate

Add a pre-check job in .gitlab-ci.yml to fail fast when disk is low — avoids builds dying halfway and leaving useless artifacts:

# .gitlab-ci.yml
check_disk:
  stage: .pre
  script:
    - |
      AVAIL=$(df -k / | awk 'NR==2 {print $4}')
      THRESHOLD=$((10 * 1024 * 1024))  # 10 GB in KB
      if [ "$AVAIL" -lt "$THRESHOLD" ]; then
        echo "ERROR: Less than 10 GB free (current: $((AVAIL/1024/1024)) GB) — clean up first"
        exit 1
      fi
      echo "Disk check passed — free: $((AVAIL/1024/1024)) GB"
  tags:
    - macos-runner

Three-option comparison

Option Best for Setup cost Cache reuse
A: Fixed path + incremental cache Mid/large projects, frequent PRs, build-time sensitive Medium (cache action config) High (cross-job reuse)
B: Periodic stale subdirectory cleanup Multi-project shared nodes, moderate disk headroom Low (one find command) Medium (same-day reuse)
C: Capacity gate fail-fast Any scenario — last-line defense Very low (5-line script) Does not affect reuse

Recommended: stack all three — A for speed, B to control growth, C as the safety net.

Automated maintenance: scheduled scripts + capacity alerts

Manual cleanup is unreliable. Wire cleanup into a script and schedule it with macOS launchd — turn disk problems from reactive firefighting into proactive defense.

Full maintenance script

#!/bin/bash
# xcode-maintenance.sh
# Recommended: run every Sunday at 3 a.m.
# Place at: ~/scripts/xcode-maintenance.sh

LOGFILE=~/Library/Logs/xcode-maintenance.log
THRESHOLD_GB=20  # Trigger deep clean when free disk falls below this

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOGFILE"; }

log "=== Xcode scheduled maintenance started ==="

# 1. Record disk state before cleanup
BEFORE=$(df -h / | awk 'NR==2 {print $4}')
log "Free disk before cleanup: $BEFORE"

# 2. Remove DerivedData subdirectories not accessed in 14 days
log "Cleaning DerivedData unused for 14+ days..."
find ~/Library/Developer/Xcode/DerivedData \
  -maxdepth 1 -mindepth 1 -type d -atime +14 \
  -exec rm -rf {} + 2>/dev/null
log "✓ Stale DerivedData entries removed"

# 3. Delete unavailable simulators
log "Cleaning unavailable simulators..."
xcrun simctl delete unavailable 2>/dev/null
log "✓ Unavailable simulators deleted"

# 4. Clean SPM cache (older than 30 days)
find ~/Library/Caches/org.swift.swiftpm -maxdepth 2 \
  -type f -atime +30 -delete 2>/dev/null
log "✓ Stale SPM cache removed"

# 5. If free space below threshold, run deep clean
AVAIL_GB=$(df -g / | awk 'NR==2 {print $4}')
if [ "$AVAIL_GB" -lt "$THRESHOLD_GB" ]; then
  log "⚠️  Free disk ${AVAIL_GB}GB < ${THRESHOLD_GB}GB — running deep clean"
  rm -rf ~/Library/Developer/Xcode/DerivedData/*
  rm -rf ~/Library/Caches/com.apple.dt.Xcode/*
  rm -rf ~/Library/Developer/Xcode/UserData/Previews/*
  log "✓ Deep clean complete"
fi

AFTER=$(df -h / | awk 'NR==2 {print $4}')
log "Free disk after cleanup: $AFTER"
log "=== Maintenance complete ==="

Register as a launchd scheduled job

Schedule for every Sunday at 3 a.m.:

# 1. Make script executable
chmod +x ~/scripts/xcode-maintenance.sh

# 2. Create launchd plist
cat > ~/Library/LaunchAgents/com.myteam.xcode-maintenance.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.myteam.xcode-maintenance</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/bash</string>
    <string>/Users/YOUR_USER/scripts/xcode-maintenance.sh</string>
  </array>
  <key>StartCalendarInterval</key>
  <dict>
    <key>Weekday</key><integer>0</integer>
    <key>Hour</key><integer>3</integer>
    <key>Minute</key><integer>0</integer>
  </dict>
  <key>StandardOutPath</key>
  <string>/tmp/xcode-maintenance-stdout.log</string>
  <key>StandardErrorPath</key>
  <string>/tmp/xcode-maintenance-stderr.log</string>
</dict>
</plist>
EOF

# 3. Load
launchctl load ~/Library/LaunchAgents/com.myteam.xcode-maintenance.plist
echo "Scheduled job registered"
Measured result: A team deployed this script on four self-hosted Mac Mini CI nodes and had zero disk alerts for three consecutive months. Weekly cleanup recovers 3–5 GB; nodes stay above 60 GB free consistently.

How cloud Mac eliminates disk pain at the root

Everything above mitigates within a fixed disk budget. If your team is evaluating CI infrastructure upgrades, one option deserves serious consideration: replace local nodes with dedicated cloud Mac Mini instances.

Disk management works differently in the cloud:

  • Add expanded SSD: Macstripe M4 Mac Mini supports extra storage — buying more disk beats manual cleanup
  • Periodic node reset: Monthly nodes can be reprovisioned clean at period end — DerivedData starts from zero, no cleanup scripts needed
  • Scale on demand: Add nodes during release sprints, scale back in quiet periods — no paying for peak capacity year-round
  • SSH direct debugging: When a build goes weird, ssh runner@your-node and troubleshoot on the machine — easier than remote-managing physical hardware

Real case: a mobile team replaced three local Mac Minis with Macstripe cloud nodes and saved roughly 8–12 hours per month on ops (disk cleanup, Xcode updates, runner troubleshooting). At engineer hourly rates, ops savings alone covered most of the rental cost.

Macstripe cloud Mac: Dedicated physical M4 Mac Mini, five global regions (Singapore / Japan / South Korea / Hong Kong / US West), SSH + VNC access, live in ~5 minutes, no long contract — pay by the day. View plans & starting prices →

Teams already on GitHub Actions can also read Can You Develop iOS on Windows Without Buying a Mac? A Hands-On Test for a full comparison of building iOS apps across platforms.

FAQ

Q: Will deleting DerivedData affect my source code or release history?

No. DerivedData only stores build artifacts, SourceKit indexes, and build logs — completely independent of source code, Git history, Provisioning Profiles, and App Store Connect submissions. Xcode rebuilds everything on the next build; the only cost is a full instead of incremental build — about 2–5 extra minutes for a mid-size project.

Q: Can I delete the Archives folder freely?

Not without checking first. .xcarchive files in Archives contain dSYM symbol files used to symbolicate production crash logs. Before deleting: ① confirm the version is off the App Store with no active users; ② or upload dSYM to Firebase Crashlytics / Sentry first. Keep Archives for the last 3 release versions; older ones can go after dSYM export.

Q: CI rebuilds from scratch every time — can I reuse cache?

Yes. Pass -derivedDataPath /tmp/derived-data to xcodebuild, then use GitHub Actions actions/cache or GitLab CI cache: for incremental sync. Cache keys must include hashes of Package.resolved and project.pbxproj to avoid cross-version pollution. For Swift Package-heavy projects, incremental builds typically save 40–60% of time.

Q: xcodebuild fails because disk is full — how do I recover quickly?

Run in priority order: ① rm -rf ~/Library/Developer/Xcode/DerivedData/* (usually frees 10–60 GB); ② xcrun simctl delete unavailable (frees 5–20 GB); ③ delete iOS DeviceSupport entries more than two versions old (frees 5–15 GB). Step ① alone fixes most cases.

Q: What's the difference between Product > Clean Build Folder and deleting DerivedData from the command line?

Clean Build Folder only clears build artifacts for the currently open project — no effect on other projects' caches, simulator data, or DeviceSupport. rm -rf ~/Library/Developer/Xcode/DerivedData/* clears all projects' caches. Use the command line for CI node maintenance; Clean Build Folder is more precise for day-to-day dev machine use.

Q: DerivedData grows very fast on a CI runner — what should I do?

Common cause: multiple Schemes / branches building concurrently, each creating a new DerivedData subdirectory. Fixes: ① use -derivedDataPath to pin a single path; ② add find ... -atime +7 -exec rm -rf {} + in your build script to remove subdirectories unused for 7 days; ③ evaluate upgrading node disk or Macstripe cloud Mac expanded SSD options.

Summary

DerivedData disk blockage is something almost every iOS developer hits — but it is fully solvable with a systematic approach, not frantic cleanup every time builds act up.

  • Understand the root cause: DerivedData is pure cache — safe to delete; Archives hold dSYM — handle carefully
  • Diagnose fast: du -sh ~/Library/Developer/Xcode/* | sort -rh — one command to find the culprit
  • Clean by category: DerivedData — delete freely; DeviceSupport — remove old, keep new; Archives — export dSYM first
  • CI protection: fixed -derivedDataPath + incremental cache + capacity gate — three layers
  • Automate: mount a maintenance script via launchd, weekly cleanup — shift from reactive to proactive
  • Fix at the root: evaluate cloud Mac — expanded SSD or periodic node reset ends disk anxiety

If your team faces frequent CI disk alerts and mid-build failures, run the diagnostic commands in this article first — you can usually free 30 GB or more in 10 minutes. Long term, moving CI to dedicated cloud Mac Mini is the more sustainable path — scale on demand without disk maintenance overhead.

Related reading