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 |
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
É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 /
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 }}-
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"
É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-nodepour 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.
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 -rhen une commande - Traiter : DerivedData sans hésiter ; DeviceSupport : garder le récent ; Archives : dSYM d'abord
- Protéger CI :
-derivedDataPathfixe + 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.