Développeur Mac diagnostiquant l'occupation disque de DerivedData dans le terminal

Deux heures du matin, alerte CI : moins de 2 Go libres sur le nœud de build, xcodebuild quitte immédiatement. Vous ouvrez le terminal, df -h — le SSD de 256 Go est plein. ~/Library/Developer monopolise 180 Go, dont plus de 90 Go pour DerivedData seul.

Ce n'est pas un incident isolé. Sur un M4 Mac Mini tournant 8 mois sans nettoyage, DerivedData atteint en moyenne 40–100 Go ; avec images simulateur et symboles appareil, ~/Library/Developer dépasse souvent 150 Go. Cet article couvre analyse des causes → localisation rapide → traitement par dossier → prévention CI → maintenance automatisée — un guide opérationnel prêt à l'emploi.

À la fin : usage et sécurité de chaque sous-dossier DerivedData, une commande pour trouver le plus gros consommateur, scripts de nettoyage local/CI, et une automatisation pour ne plus revoir d'alerte disque.

Quick Answer : les trois questions les plus fréquentes

Question Réponse directe Risque
Peut-on supprimer DerivedData directement ? rm -rf ~/Library/Developer/Xcode/DerivedData/* — totalement sûr Prochain build complet, +2–5 min environ
Peut-on supprimer Archives ? Versions sans utilisateurs actifs : oui ; versions en prod : garder les dSYM Impossible de symboliser les crashs d'anciennes versions
Urgence disque sur un nœud CI ? DerivedData → simulateurs unavailable → anciens DeviceSupport, dans cet ordre Reconstruction d'index au prochain build, ~3 min de plus

Que contient vraiment DerivedData

Beaucoup savent que « vider DerivedData règle des bugs obscurs » sans savoir ce qu'il y a dedans — on supprime le mauvais ou on garde ce qui fait exploser le disque. Tableau des chemins essentiels sous ~/Library/Developer :

Chemin Contenu Taille typique Suppression sûre ?
Xcode/DerivedData Artefacts de build, index, journaux, cache modules Swift 5–60 Go ✅ Totalement sûr, reconstruction auto au prochain build
Xcode/Archives Builds release (.xcarchive) avec dSYM 2–30 Go ⚠️ Anciennes versions OK ; versions actives : garder dSYM
Xcode/iOS DeviceSupport Symboles de debug téléchargés à la connexion d'un appareil 1–3 Go / version iOS ✅ Anciennes versions OK, re-téléchargement à la reconnexion
CoreSimulator/Profiles/Runtimes Images runtime simulateur 5–10 Go / version iOS ⚠️ Versions inutilisées supprimables, re-téléchargement requis
CoreSimulator/Devices Données des instances simulateur créées 1–15 Go ✅ État unavailable : suppression sûre
Caches/org.swift.swiftpm Cache paquets SPM 1–8 Go ✅ Supprimable, reconstruction au prochain resolve
Caches/com.apple.dt.Xcode Cache interne Xcode (previews, IB) 0,5–3 Go ✅ Supprimable
Conclusion clé : tout sous DerivedData est du cache reconstructible, indépendant du code source, des profils de provisioning et des soumissions App Store. Seul coût : prochain build complet (+2–5 min sur un projet moyen).

Structure des sous-dossiers DerivedData

Chaque projet a un sous-dossier {NomProjet}-{hash aléatoire}/. Structure interne :

MyApp-abcdefghijklmn/
├── Build/
│   ├── Products/          # Artefacts (.app, .framework, .o)
│   └── Intermediates.noindex/  # Fichiers intermédiaires (.o, graphe .d)
├── Index.noindex/         # Index SourceKit (saut de code, autocomplétion)
├── Logs/                  # Journaux de build (xcodebuild -resultBundlePath aussi ici)
└── info.plist             # Enregistrement du chemin projet

Build/Intermediates.noindex est souvent le plus gros — un projet moyen avec 200 fichiers Swift dépasse facilement 2 Go ; avec Index.noindex, 5–10 Go par projet n'est pas rare.

Cinq étapes pour localiser le blocage disque

Ne nettoyez pas au hasard. Trois minutes de diagnostic valent mieux qu'une suppression aveugle. Ce flux, testé sur un M4 Mac Mini 256 Go, trouve le top 5 en moins de 2 minutes.

Étape 1 : vue d'ensemble du disque

# Utilisation globale du disque
df -h /

# Top 10 dossiers à la racine
sudo du -sh /* 2>/dev/null | sort -rh | head -10

Étape 2 : répartition du dossier Developer

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

Exemple mesuré (nœud CI après 8 mois) :

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

Étape 3 : les plus gros sous-projets DerivedData

du -sh ~/Library/Developer/Xcode/DerivedData/* 2>/dev/null | sort -rh | head -10
Chiffres mesurés : sur une machine avec 15 projets historiques, le top 3 DerivedData : 18 Go, 12 Go, 9 Go — deux projets non ouverts depuis plus d'un an occupaient 30 Go inutilement.

Étape 4 : occupation et état des simulateurs

# Lister tous les simulateurs et leur état
xcrun simctl list devices

# Voir uniquement les "unavailable" (restes après upgrade système)
xcrun simctl list devices | grep -i "unavailable"

Étape 5 : distribution des versions DeviceSupport

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

Si la sortie ressemble à ceci, des années de symboles se sont accumulées :

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)

En 2026, les symboles iOS 15 et 16 ont peu d'intérêt (minimum App Store souvent iOS 17+) — suppression sans risque.

Stratégie par dossier : supprimer, prudence, ne pas toucher

Suppression directe (zéro risque)

# 1. Vider le cache de build de tous les projets
rm -rf ~/Library/Developer/Xcode/DerivedData/*

# 2. Supprimer les simulateurs unavailable
xcrun simctl delete unavailable

# 3. Nettoyer le cache paquets SPM
rm -rf ~/Library/Caches/org.swift.swiftpm/*

# 4. Nettoyer le cache interne Xcode
rm -rf ~/Library/Caches/com.apple.dt.Xcode/*

# 5. Nettoyer le cache previews SwiftUI
rm -rf ~/Library/Developer/Xcode/UserData/Previews/*

Suppression sélective (garder le récent)

# Supprimer les symboles appareil de plus de 2 versions (garder les 2 dernières iOS)
# D'abord lister les versions présentes
ls ~/Library/Developer/Xcode/iOS\ DeviceSupport/

# Exemple : ne garder que iOS 18.x
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*

À traiter avec prudence : Archives

  • Confirmer que la version est retirée de l'App Store ou sans utilisateurs actifs
  • Si des utilisateurs restent : exporter les dSYM (Organizer → clic droit Archive → Show in Finder → récupérer .dSYM)
  • Envoyer les dSYM sur Firebase Crashlytics / Sentry ou autre plateforme crash
  • Supprimer l'Archive correspondante une fois les dSYM reçus

Commande d'urgence (disque critique)

Exécuter dans l'ordre ci-dessous libère généralement 20–60 Go rapidement :

#!/bin/bash
# emergency-clean-xcode.sh
# Avant exécution : aucun processus xcodebuild en cours
echo "=== Nettoyage d'urgence disque Xcode ==="

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

echo "[2/4] Suppression simulateurs unavailable..."
xcrun simctl delete unavailable
echo "✓ Simulateurs unavailable supprimés"

echo "[3/4] Nettoyage cache SPM et Xcode..."
rm -rf ~/Library/Caches/org.swift.swiftpm/*
rm -rf ~/Library/Caches/com.apple.dt.Xcode/*
echo "✓ Caches nettoyés"

echo "[4/4] Nettoyage cache previews..."
rm -rf ~/Library/Developer/Xcode/UserData/Previews/*
echo "✓ Cache previews nettoyé"

echo ""
echo "=== Nettoyage terminé, état disque actuel ==="
df -h /
💡 Solution structurelle : si votre nœud CI est une machine physique locale, la gestion disque reste un fardeau permanent. Macstripe propose un M4 Mac Mini cloud dédié, facturation jour/semaine/mois, réinitialisation du nœud à la fin d'usage — l'accumulation DerivedData disparaît à la source.Voir les tarifs →

Prévenir le blocage en pipeline CI/CD

En local, le nettoyage manuel passe encore ; un runner CI sans surveillance exige une protection systématique. Trois approches pour un runner macOS auto-hébergé (GitHub Actions / GitLab CI).

Option A : chemin DerivedData fixe + cache incrémental

Pointer DerivedData vers un chemin contrôlé et réutiliser via le cache CI :

# Extrait .github/workflows/ios-build.yml
- 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 }}-
Attention : la clé de cache doit inclure les hash de Package.resolved et project.pbxproj, sinon une mise à jour Swift Package peut réutiliser un vieux cache et faire échouer le build.

Option B : nettoyage auto des caches obsolètes avant/après build

# Avant le build : supprimer les sous-dossiers DerivedData non utilisés depuis 7 jours
- name: Clean stale DerivedData
  run: |
    find ~/Library/Developer/Xcode/DerivedData \
      -maxdepth 1 -mindepth 1 \
      -type d \
      -atime +7 \
      -exec rm -rf {} +
    echo "DerivedData nettoyé, taille actuelle :"
    du -sh ~/Library/Developer/Xcode/DerivedData 2>/dev/null || echo "Dossier vide"

Option C : garde-fou capacité disque GitLab CI

Ajouter un job de pré-vérification dans .gitlab-ci.yml pour fail-fast si le disque est insuffisant — évite un build qui plante à mi-parcours :

# .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: Moins de 10 Go libres (actuel : $((AVAIL/1024/1024)) Go), nettoyez d'abord"
        exit 1
      fi
      echo "Contrôle disque OK, libre : $((AVAIL/1024/1024)) Go"
  tags:
    - macos-runner

Comparaison des trois options

Option Cas d'usage Coût d'implémentation Taux de réutilisation cache
A : chemin fixe + cache incrémental Projets moyens/grands, PR fréquentes, build sensible au temps Moyen (config cache action) Élevé (réutilisation inter-jobs)
B : nettoyage périodique sous-dossiers Nœud partagé multi-projets, disque moyennement disponible Faible (une ligne find) Moyen (réutilisation jour même)
C : garde-fou capacité fail-fast Tout contexte, filet de sécurité Très faible (5 lignes de script) N'affecte pas la réutilisation

Recommandation : combiner les trois — A pour la vitesse, B pour limiter la croissance, C comme dernier rempart.

Maintenance automatisée : scripts planifiés + alertes capacité

Le nettoyage manuel finit toujours par être oublié. Script + launchd sur macOS : passage de la réaction d'urgence à la défense proactive.

Script de maintenance complet

#!/bin/bash
# xcode-maintenance.sh
# Recommandé : chaque dimanche à 3 h
# Emplacement : ~/scripts/xcode-maintenance.sh

LOGFILE=~/Library/Logs/xcode-maintenance.log
THRESHOLD_GB=20  # Déclenche nettoyage profond si espace libre sous ce seuil

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

log "=== Début maintenance Xcode planifiée ==="

# 1. État disque avant nettoyage
BEFORE=$(df -h / | awk 'NR==2 {print $4}')
log "Espace libre avant : $BEFORE"

# 2. Supprimer sous-dossiers DerivedData non accédés depuis 14 jours
log "Nettoyage DerivedData > 14 jours..."
find ~/Library/Developer/Xcode/DerivedData \
  -maxdepth 1 -mindepth 1 -type d -atime +14 \
  -exec rm -rf {} + 2>/dev/null
log "✓ Anciennes entrées DerivedData supprimées"

# 3. Supprimer simulateurs unavailable
log "Nettoyage simulateurs unavailable..."
xcrun simctl delete unavailable 2>/dev/null
log "✓ Simulateurs unavailable supprimés"

# 4. Nettoyer cache SPM (> 30 jours)
find ~/Library/Caches/org.swift.swiftpm -maxdepth 2 \
  -type f -atime +30 -delete 2>/dev/null
log "✓ Ancien cache SPM nettoyé"

# 5. Si espace sous seuil : nettoyage profond
AVAIL_GB=$(df -g / | awk 'NR==2 {print $4}')
if [ "$AVAIL_GB" -lt "$THRESHOLD_GB" ]; then
  log "⚠️  Espace libre ${AVAIL_GB}Go < ${THRESHOLD_GB}Go, nettoyage profond"
  rm -rf ~/Library/Developer/Xcode/DerivedData/*
  rm -rf ~/Library/Caches/com.apple.dt.Xcode/*
  rm -rf ~/Library/Developer/Xcode/UserData/Previews/*
  log "✓ Nettoyage profond terminé"
fi

AFTER=$(df -h / | awk 'NR==2 {print $4}')
log "Espace libre après : $AFTER"
log "=== Maintenance terminée ==="

Enregistrer comme tâche launchd planifiée

Exécution automatique chaque dimanche à 3 h :

# 1. Rendre le script exécutable
chmod +x ~/scripts/xcode-maintenance.sh

# 2. Créer le plist launchd
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. Charger
launchctl load ~/Library/LaunchAgents/com.myteam.xcode-maintenance.plist
echo "Tâche planifiée enregistrée"
Effet mesuré : une équipe a déployé ce script sur 4 Mac Mini CI auto-hébergés — zéro alerte disque en 3 mois. Nettoyage hebdomadaire de 3–5 Go, nœuds stables au-dessus de 60 Go libres.

Éviter le problème disque avec un Mac cloud

Toutes ces approches atténuent sous contrainte de « disque limité ». Si vous évaluez votre infra CI, une option mérite attention : remplacer les nœuds locaux par des Mac Mini cloud dédiés.

En cloud, la gestion disque prend d'autres chemins :

  • SSD étendu : le M4 Mac Mini Macstripe supporte le stockage étendu — acheter plus de disque bat nettoyer manuellement
  • Réinitialisation périodique : un nœud facturé au mois peut être recréé propre en fin de cycle — DerivedData repart de zéro, sans script de nettoyage
  • Montée en charge à la demande : plus de nœuds en période de release, moins en creux — pas de surcoût permanent pour le pic
  • Debug SSH direct : problème de build obscur ? ssh runner@your-node pour investiguer, plus simple qu'une machine physique distante

Cas réel : une équipe mobile a remplacé 3 Mac Mini locaux par des nœuds Macstripe — ~8–12 h/mois économisées en nettoyage disque, mises à jour Xcode et dépannage runner. Au taux horaire ingénieur, l'économie opérationnelle couvre une large part de la location.

Mac cloud Macstripe : Mac Mini M4 physique dédié, cinq régions (Singapour / Japon / Corée / Hong Kong / Ouest US), SSH + VNC, provisionnement ~5 min, facturation à la journée sans engagement long.Voir forfaits et tarifs →

Pour les équipes déjà sur GitHub Actions, voir aussi Développer iOS sur Windows — en finir avec l'achat d'un Mac ? pour une comparaison complète des plateformes de build iOS.

FAQ

Q : Supprimer DerivedData affecte-t-il le code source ou l'historique des releases ?

Non. DerivedData ne contient que des artefacts de build, index SourceKit et journaux — indépendant du code, Git, profils de provisioning et soumissions App Store Connect. Xcode reconstruit au prochain build ; seul coût : build complet plutôt qu'incrémental (+2–5 min sur projet moyen).

Q : Peut-on supprimer Archives sans réfléchir ?

Non sans précaution. Les .xcarchive contiennent les dSYM pour symboliser les crashs en production. Avant suppression : ① version retirée de l'App Store sans utilisateurs actifs, ou ② dSYM envoyés sur Firebase Crashlytics / Sentry. Garder les 3 dernières releases ; au-delà, exporter dSYM puis supprimer.

Q : En CI, rebuild complet à chaque fois — peut-on réutiliser le cache ?

Oui. Passer -derivedDataPath /tmp/derived-data à xcodebuild, puis actions/cache (GitHub) ou cache: (GitLab) pour sync incrémentale. Clé incluant hash Package.resolved et project.pbxproj. Sur projets riches en Swift Package, build incrémental : 40–60 % de temps en moins mesuré.

Q : Disque plein, xcodebuild en échec — récupération rapide ?

Par priorité : ① rm -rf ~/Library/Developer/Xcode/DerivedData/* (souvent 10–60 Go) ; ② xcrun simctl delete unavailable (5–20 Go) ; ③ supprimer entrées iOS DeviceSupport de plus de deux versions (5–15 Go). L'étape ① suffit dans la majorité des cas.

Q : « Product > Clean Build Folder » vs suppression DerivedData en CLI ?

Clean Build Folder ne nettoie que le projet ouvert — pas les autres projets, simulateurs ni DeviceSupport. rm -rf ~/Library/Developer/Xcode/DerivedData/* efface tous les caches. CLI pour maintenance CI ; Clean Build Folder plus ciblé au quotidien.

Q : DerivedData grossit très vite sur un runner CI ?

Souvent : plusieurs schemes/branches en parallèle, chaque build crée un sous-dossier DerivedData. ① -derivedDataPath vers un chemin unique ; ② find ... -atime +7 -exec rm -rf {} + dans le script de build ; ③ évaluer extension disque ou SSD étendu Macstripe cloud.

Conclusion

Le blocage disque DerivedData touche presque tous les développeurs iOS — mais il se résout de façon systématique, sans paniquer à chaque « bug mystérieux ».

  • Comprendre : DerivedData = cache pur, suppression sûre ; Archives = dSYM, prudence
  • Diagnostiquer : du -sh ~/Library/Developer/Xcode/* | sort -rh en une commande
  • Traiter : DerivedData sans hésiter ; DeviceSupport : garder le récent ; Archives : dSYM d'abord
  • Protéger CI : -derivedDataPath fixe + cache incrémental + garde-fou capacité
  • Automatiser : script launchd hebdomadaire, défense proactive
  • Résoudre à la source : Mac cloud avec SSD étendu ou reset périodique

Si votre équipe subit alertes disque CI et builds interrompus, lancez d'abord le script de diagnostic de cet article — souvent 30 Go+ libérés en 10 minutes. À long terme, migrer l'infra CI vers un Mac Mini cloud dédié est plus durable : montée en charge à la demande, sans maintenance disque.

Lectures associées