Stop new writes as soon as Xcode No space left on device appears, capture the disk and process state, then remove only confirmed disposable data. This applies when the failed remote Mac CI job can be reproduced after cleanup; if normal builds, tests, and archives still consume the node, change retention, isolate workloads, or expand the node instead of repeating a full-disk purge.

This week’s action plan: freeze the affected runner today, preserve the first failure evidence, assign each storage area to an owner, and validate recovery with the same commit and build settings before returning the node to service.

This guide is for:

  • On-call developers restoring a broken Xcode build with the lowest-risk sequence.
  • DevOps engineers maintaining a shared Mac Runner across projects, caches, and Simulator test jobs.
  • Platform engineers deciding whether to clean, rebuild, isolate, or expand a node.
01

Incident containment

A disk-full failure is not a normal cache-cleaning task. It is a state-preservation task first. A build may have failed while writing an object file, an index, a test result, an archive, or diagnostic output. Deleting the entire development directory can remove the evidence needed to identify the owner of the growth.

Stop the scheduler or disable new jobs for the affected runner. Do not launch another build to “see whether it was temporary.” A second job can overwrite useful evidence, create another partial checkout, or consume the last available blocks.

Record the following before removal:

  • The first complete No space left on device message from the build log.
  • The failed job identifier, commit, scheme, destination, and build settings.
  • The execution account and active Xcode selection.
  • The jobs and processes still running on the node.
  • The system volume and relevant data-volume state.
  • The directories or files that grew during the failed job.

Apple’s Mac storage guidance recommends reviewing storage categories and removing files based on their purpose rather than treating every large item as disposable. Use the Apple storage management guidance as a general reference, but apply it to the runner’s actual accounts and pipeline settings.

A first capture can be non-destructive:

date
whoami
xcode-select -p
df -h
df -i
ps auxww
du -xhd 1 "$HOME" 2>/dev/null | sort -h

Example output should be treated as evidence, not as a cleanup instruction:

<timestamp>
<ci-user>
<active-developer-directory>
<filesystem-capacity-and-available-space>
<inode-or-file-count-state>
<running-build-and-test-processes>
<top-level-directory-usage>

If the filesystem has free blocks but file creation still fails, inspect inode or file-count pressure, per-directory limits, and the specific path named by the error. If a directory grows while the same job remains active, stop the job before considering removal. If space returns briefly and disappears during every build, hand the evidence to the node maintainer rather than repeating deletion.

02

On-call recovery

The on-call developer owns the first recovery decision: restore the workspace without destroying deliverables. Start with the failed project’s checkout, temporary build locations, duplicate clones, and outputs left by stopped jobs.

Separate the workspace into three categories:

  • Source code, lockfiles, project settings, and scripts that must remain.
  • Reproducible intermediates that can be regenerated from the same commit.
  • Deliverables or evidence, such as test results, exported packages, diagnostics, and release artifacts, whose retention depends on the team’s process.

Use the actual workspace and account supplied by the runner configuration. Do not assume that a familiar local path is used by a remote job. A hosted agent may use a service account, a custom working directory, or a per-job checkout path.

A safe inspection pattern is:

find "<workspace-root>" -maxdepth 2 -type d -print
du -xhd 1 "<workspace-root>" 2>/dev/null | sort -h
find "<workspace-root>" -type f -mmin -60 -print

The time filter above is only an investigation aid. It does not prove that a file is disposable. A recently modified archive may be the release output, while an older cache may still be shared by a running process.

After confirming that the failed task has stopped, remove only abandoned, reproducible output identified by the project owner or pipeline definition. Keep the commit, build configuration, and destination unchanged for the recovery test. A successful build with different settings does not prove that the original failure was fixed.

The recovery handoff is complete only when:

  • The original workspace remains available for comparison.
  • The removed paths and reason for removal are recorded.
  • The same job can start without an immediate write failure.
  • The build reaches the stage that previously failed.
  • Any remaining growth is assigned to a cache, test, archive, or platform owner.

If available space drops rapidly again, stop the repeated build loop. A runner that is being filled by normal output requires capacity governance, not more emergency deletion.

03

Cache ownership

The build maintainer owns DerivedData, dependency caches, package-manager data, and tool caches. These areas often look similar during an incident, but they have different regeneration costs and ownership rules.

DerivedData is not one universal directory with one universal lifecycle. Its location depends on the active Xcode workflow, execution account, workspace settings, and CI parameters. Confirm the path from the job configuration and the active process before removing anything.

Inspect first:

xcodebuild -version
xcodebuild -showBuildSettings \
  -workspace "<workspace>.xcworkspace" \
  -scheme "<scheme>" \
  -configuration "<configuration>" \
  | tee "<evidence-directory>/build-settings.txt"

The Xcode command-line tool reference documents the supported command-line surface. The local help output remains important after an Xcode or macOS update because available options and behavior can change.

Before removing DerivedData or a package cache, confirm:

  • No build, index, test, archive, or dependency-resolution process is using it.
  • The cache belongs to the failed project or runner policy.
  • The next build has access to the required dependency sources.
  • The team accepts the cost of re-resolution or recompilation.
  • The cleanup can be reversed by rerunning the defined pipeline.

Do not treat every cache as garbage. A cache can reduce repeated work, but it can also contain stale or corrupted state. The correct test is not merely whether xcodebuild returns success. Run a clean build using the original commit and settings, then run a second build that is expected to reuse the permitted cache. Compare the stages that previously failed and inspect whether the next job causes uncontrolled growth.

Homebrew, Swift Package, language-tool, and other dependency caches should be handled under their own retention policies. A shared runner needs an owner for each cache, an age or job-based cleanup event, and a recovery path when the cache is removed. If no owner can explain why a cache exists, quarantine its inventory before deleting it.

04

Simulator and platform data

The test owner handles iOS Simulator storage. The main distinction is between installed runtime components, simulator device instances, and test-generated data. Removing the wrong category can make the current test matrix unavailable even if the disk immediately looks healthier.

First inspect the installed components and devices through the supported Xcode interface or matching local command help. Apple documents additional Xcode component management, running apps on simulated devices, and managing simulated and physical devices.

A command-level inventory may look like this:

xcrun simctl list runtimes
xcrun simctl list devices
xcrun simctl list devices unavailable

The output must be saved before any removal:

<runtime identifier and availability>
<device identifier, name, state, and runtime>
<unavailable device entries>

Keep every runtime required by the active test matrix. A runtime is a platform component; a device instance is a test target built on that platform. They should not share one automatic deletion rule.

A device can also contain test-generated application data, logs, screenshots, databases, and crash material. Remove those only when the test owner confirms that the result has been uploaded or is no longer needed. Do not directly modify protected system directories simply because they appear large. Use Xcode’s supported management entry points or the command options shown by the local simctl help.

After cleanup, validate the complete path:

  • The required runtime is still listed as available.
  • A new test device can boot.
  • The test application installs and launches.
  • The relevant test suite completes.
  • Test results and diagnostics are collected.
  • The job can recover after the remote session disconnects.

This is where the iOS Simulator long-tail problem becomes an operational decision rather than a deletion exercise. If the test matrix itself requires more installed runtimes and device data than the node can retain, the platform owner must redesign the runner pool or increase storage capacity.

05

Release asset protection

The release owner must protect archives and signing material even when the archive job caused the incident. An .xcarchive, exported package, symbol file, upload staging area, or notarization-related artifact may be part of the delivery record.

Classify each release item by state:

  • Failed and reproducible output that has never been used for delivery.
  • Completed archive retained for review, export, or rollback.
  • Exported package awaiting upload or verification.
  • Symbols and diagnostics required for post-release investigation.
  • Signing assets required by the next build or release process.

Apple’s archive and distribution documentation describes the supported release flow. Its debugging information guidance also explains why debug symbols and related information can matter after delivery.

Never use the same automatic cleanup rule for archives and DerivedData. DerivedData is generally reproducible within the project’s toolchain; an archive may be the exact evidence needed to validate a release. A certificate, provisioning profile, or keychain entry is not a space-saving target. Removing signing material can turn a disk incident into a release outage.

Before returning the runner to service, perform the actual release validation:

xcodebuild archive \
  -workspace "<workspace>.xcworkspace" \
  -scheme "<scheme>" \
  -archivePath "<validated-archive-path>" \
  | tee "<evidence-directory>/archive.log"

Then verify the archive, export, upload, and symbol handling expected by the pipeline. The command must match the project’s signing configuration. A local archive that bypasses the production signing path is not sufficient evidence.

06

FAQ for remote Mac CI recovery

What the first deletion target should be

The first candidate is not “the largest directory.” It is abandoned, reproducible output from a stopped job whose owner and use are known. That may include a duplicate checkout or a temporary build result, but it may not include a current archive, diagnostic bundle, or data still opened by a running process.

Whether DerivedData is safe to remove

DerivedData can be removable when no task is using it and the pipeline can regenerate it, but the decision depends on the active account, Xcode selection, workspace, and build parameters. Capture the path and ownership first. After removal, verify both a clean build and a permitted cache-reuse build.

How to handle a large iOS Simulator footprint

Keep the runtimes required by the test matrix. Separate those components from obsolete device instances and generated test data. Use supported Xcode or simctl operations, then boot a test device and execute the relevant suite. A disk-saving operation that prevents the next test from launching is not a successful repair.

How shared runners avoid another full disk

The platform owner should define retention and ownership for source checkouts, build caches, Simulator components, archives, exports, and diagnostics. Cleanup should occur at known lifecycle events, with logs and acceptance checks. A shared Mac Runner should not depend on a human remembering to delete its entire disk after each incident.

When cleanup should become expansion

If required source, cache, runtime, test, and release retention exceeds the node’s usable capacity during normal operation, cleanup only postpones the next failure. Split build and release workloads, reduce idle retention where policy allows, or select a remote Mac node with a storage configuration that matches the workload.

07

Platform capacity governance

The platform owner closes the incident by converting its evidence into policy. Start with a per-project and per-job inventory. Each storage category needs an owner, a retention reason, a cleanup trigger, and an acceptance check.

Use this decision checklist before making the final change:

  • [ ] New jobs are paused, and no cleanup command can affect an active build, test, archive, or dependency-resolution process.
  • [ ] The first useful error, filesystem state, execution account, active Xcode selection, and running processes are saved.
  • [ ] Workspace files are separated from reproducible intermediates, diagnostics, archives, exported packages, and signing assets.
  • [ ] DerivedData and dependency caches have been traced to the actual project, account, workspace, and pipeline settings.
  • [ ] Installed Simulator runtimes required by the current test matrix are marked for retention.
  • [ ] Obsolete device instances or test data have an owner’s approval before removal.
  • [ ] Any cleanup target is reproducible, no longer in use, and recorded with its path and reason.
  • [ ] The original commit, scheme, destination, and build settings are used for the recovery build.
  • [ ] A second build verifies the expected cache behavior rather than checking only the first command’s exit status.
  • [ ] Simulator boot, application installation, test execution, result collection, and remote-session recovery are verified.
  • [ ] Archive, export, upload, symbol, and signing paths are tested if the node serves release work.
  • [ ] The final outcome is explicitly classified as clean, isolate, or expand.
  • [ ] If normal retention exceeds capacity, the team stops repeating full-disk deletion and opens a node-sizing or workload-isolation change.

A workable policy separates:

  • Workspace retention after a job completes.
  • Cache retention and invalidation after toolchain changes.
  • Simulator runtime retention for the active test matrix.
  • Device and test-result retention after result upload.
  • Archive and export retention after release verification.
  • Reboot or maintenance checks for stale processes and temporary files.

Use the same job to validate the policy that caused the incident. The acceptance record should include the original commit, scheme, destination, test matrix, archive path, export behavior, and recovery behavior after a remote connection interruption. A node that survives a manual cleanup but fails the normal pipeline has not been restored.

The remote Mac CI capacity decision has three outcomes:

  • Clean: abandoned and reproducible data caused the failure, and the normal workload remains within policy.
  • Isolate: different projects or release stages interfere with each other, so build, test, and release workloads need separate retention or runner boundaries.
  • Expand or replace: required data remains after policy cleanup, or the workload repeatedly consumes the node during normal execution.

Do not rebuild the node merely because the filesystem reached its limit. Rebuilding can destroy archives, diagnostics, signing configuration, and evidence. Rebuild only when the platform team has exported required assets, documented the configuration, and confirmed that the node state itself is corrupt or unmanageable.

The safest Xcode No space left on device repair is therefore a controlled sequence: freeze writes, preserve evidence, identify the data owner, remove only confirmed reproducible content, and rerun the real workflow. When normal retention still exceeds capacity, a larger or better-isolated node is the engineering fix.

If the current setup relies on repeated manual cleanup, it has three persistent weaknesses: active jobs can compete with maintenance, archives and caches can be governed by the same unsafe rule, and a full disk can interrupt both diagnosis and release delivery. A remote Mac rental gives the team a cleaner way to test a real project against a different storage configuration and recovery workflow before committing to a long-lived node design. NodeMini’s remote Mac options can be evaluated with the same build, Simulator, archive, and restart acceptance steps rather than by trusting a specification alone.

For teams that need to compare an alternative node before changing production CI, the NodeMini service overview is the appropriate starting point. Keep the existing node when its normal workload fits a documented retention policy; choose a different configuration or rental period only when the real project proves that cleanup and isolation cannot keep the workflow within capacity.