30 Sekunden (Priorität)
- Hauptengpass: Pods (Netz + Auflösung) > DerivedData > Flutter cache — zuerst Pods.
- Einzelursache: nicht „alle drei Ebenen langsam“, sondern Upstream-Miss zwingt Downstream-Neuarbeit (§1).
- Eine Regel (§0): Cache an Lockfiles/SDK, nicht an Datum.
- Wenig Zeit: §0+§0.1+§4 CocoaPods; Hot-Build langsam → §5.
- §7: Restore-Reihenfolge schlägt clevere Keys.
0. Das eine Cache-Prinzip
Vor CocoaPods, DerivedData, PUB_CACHE gilt für alle Keys:
CI-Cache bindet Build-Determinismus — nicht „Dateien lagern“.
pubspec.lock/Podfile.lockändern → Miss → erwartet- Xcode/Flutter-SDK-Upgrade → Miss → erwartet
- Job-Zeit, Datum, Zufallssuffix allein → nicht
Symptom: täglicher Kaltstart — Caches per Datum oder Vollwipe statt Lockfile+Version.
0.1 Echte Engpass-Reihenfolge
Alle Abschnitte folgen einem Urteil — Ebenen sind ungleich:
Time ceiling (optimize in this order):
CocoaPods (network + resolve) 🔥🔥🔥 often ~70% of ceiling
↓
DerivedData (compile increment) 🔥🔥 hot builds → ~6 min tier
↓
Flutter cache (toolchain) 🔥 mainly first-run pub / engine
Urteil: Langsamkeit ist selten drei parallele Probleme — Pods deckeln zuerst; bei Pods-Miss hilft DerivedData nur downstream ins Leere.
Lesepfad: 10 Min → §0+§0.1+§4; Cold ~15 Min, Hot langsam → §5; §6 für Image und erstes pub.
1. Einzelursache: Propagation bis IPA
Keine drei Subsysteme — eine Kette. Bruch upstream oder downstream?
Hängt flutter build ipa bei Running pod install..., §0.1: Pods-Cache/Netz vor Xcode-Flags. Messen: pub → pod → xcodebuild.
1.1 Kette
In Kausalrichtung lesen:
[start · primary] CocoaPods miss / slow private specs
│ Pod tree rebuild, Specs fetch
▼
[propagate] DerivedData forced cold (full xcodebuild)
│
▼
[end] Slow IPA (often blamed on Flutter/Dart)
Rückwärts: pubspec.lock → Plugins → pod → DerivedData weg. Restore: Pub zuerst; Debug-Budget: Pods → DerivedData → Flutter.
Zitat: ~90 % „Flutter CI langsam“ = Upstream-Kette, nicht Compiler.
Offiziell: Flutter iOS deployment · CocoaPods environment · Xcode Build Settings.
2. Benchmark: §0.1-Gewichte
Tabelle belegt Pods zuerst, DerivedData danach. kvmboot-Typisch (mittleres Flutter, ~40 Pods, Release, Cloud Mac M4); gleicher Commit.
| Stufe | Gewicht | cold | hot | Hinweis |
|---|---|---|---|---|
| Kein Cache | — | ~28 min | ~28 min | Kette offen; Hot≈Cold |
| Nur Pods+Specs | 🔥🔥🔥 | ~19 min | ~12 min | Größter Einzelschritt; zuerst |
| + DerivedData | 🔥🔥 | ~11 min | ~6 min | Hot-Build; nach Pods-Treffer |
| + Flutter pub/Engine | 🔥 | ~10 min | ~5 min | Rand; erstes pub |
Cold >20 Min → §4, nicht §5. Cold ~11, Hot >12 → §5 restore.
Selbsttest: Vier Zeilen wall time; Commit und FLUTTER_VERSION fixieren.
3. Dual-Agent auf einem Host: Grenzen
Begleitartikel vom 2026-06-03: Cloud Mac Dual-AI-Agent: Claude Code + Codex-Isolation (2026-06-03) — zwei CLIs, ein Worktree intakt. Mit Nacht-Flutter-CI auf demselben gemieteten Mac:
- Getrennte Cache-Wurzeln: CI-
DERIVED_DATA_PATH, nicht Agent-DerivedData. - Getrennte Planung: paralleles xcodebuild + Indexierung → Swap; 16 GB: Agent tags, CI nachts.
- Kein Kreuz: kein
pod updateauf CI-ios/Pods.
4. CocoaPods CI 🔥🔥🔥
Primärschicht. ~70 % Flutter iOS build slow in pod install. 28→19 Min hier — sonst rettet DerivedData nicht. §0: Podfile.lock.
4.1 Umgebung
export CP_HOME_DIR="${CP_HOME_DIR:-$HOME/.cocoapods}"
export COCOAPODS_PARALLEL_CODE_DOWNLOAD=true
# persist: ~/.cocoapods/repos + ~/Library/Caches/CocoaPods
Private Specs: GitHub Actions macOS runner / Self-hosted Cloud Mac in Git-Region. Bitrise vs Self-hosted Mac Runner.
4.2 Pods/-Baum
- Üblich:
ios/Podscachen. - Compliance:
Pods/committen,--deployment.
Kein blindes pod update.
Explizit pod install --verbose vor flutter build ipa.
5. DerivedData 🔥🔥
Nach Pods-Treffer Hot ~6 Min.
export DERIVED_DATA_PATH="/var/ci/derived/flutter-${FLUTTER_VERSION}-xcode-${XCODE_VERSION}"
xcodebuild -workspace ios/Runner.xcworkspace -scheme Runner \
-configuration Release -derivedDataPath "$DERIVED_DATA_PATH" \
-destination 'generic/platform=iOS' build
Tier-Sweep; Disk: Runner-Disk- und Inode-Governance.
6. Flutter-Toolchain 🔥
Erstlauf-Downloads.
Nicht build/ios cross-job; Docker-Aufteilung.
7. GitHub Actions Pipeline
§4–§6 als YAML.
| Nr. | Block | Pfade | Key (§0) |
|---|---|---|---|
| 1 | pub 🔥 | PUB_CACHE | pubspec.lock + Flutter SDK |
| 2–3 | pods 🔥🔥🔥 | ~/.cocoapods + ios/Pods | Podfile.lock + CP version |
| 4 | deriveddata 🔥🔥 | $DERIVED_DATA_PATH | Xcode + Flutter + lockfiles |
# .github/workflows/flutter-ios.yml (structure; macos-latest or self-hosted cloud Mac)
jobs:
build-ios:
runs-on: macos-14 # or runs-on: [self-hosted, cloud-mac]
steps:
- uses: actions/checkout@v4
# ── Restore (order: Pub → Pods Specs → Pods tree → DerivedData) ──
- name: Restore Pub Cache
uses: actions/cache/restore@v4
with:
path: ${{ env.PUB_CACHE }}
key: pub-${{ hashFiles('pubspec.lock') }}-flutter-${{ env.FLUTTER_VERSION }}
- name: Restore CocoaPods Specs
uses: actions/cache/restore@v4
with:
path: |
~/.cocoapods
~/Library/Caches/CocoaPods
key: pods-specs-${{ env.COCOAPODS_VERSION }}
- name: Restore Pods Tree
uses: actions/cache/restore@v4
with:
path: ios/Pods
key: pods-tree-${{ hashFiles('ios/Podfile.lock') }}
- name: Restore DerivedData
uses: actions/cache/restore@v4
with:
path: ${{ env.DERIVED_DATA_PATH }}
key: dd-${{ env.XCODE_VERSION }}-${{ env.FLUTTER_VERSION }}-${{ hashFiles('pubspec.lock', 'ios/Podfile.lock') }}
# ── Build ──
- name: Flutter Pub Get
run: flutter pub get
- name: Pod Install
run: cd ios && pod install --verbose
- name: Flutter Build IPA
run: flutter build ipa --release --export-options-plist=ios/ExportOptions.plist
# ── Save (mirror restore; save costly layers even on failure) ──
- name: Save Pub Cache
uses: actions/cache/save@v4
if: always()
with:
path: ${{ env.PUB_CACHE }}
key: pub-${{ hashFiles('pubspec.lock') }}-flutter-${{ env.FLUTTER_VERSION }}
- name: Save Pods + DerivedData
uses: actions/cache/save@v4
if: always()
with:
path: |
~/.cocoapods
~/Library/Caches/CocoaPods
ios/Pods
${{ env.DERIVED_DATA_PATH }}
key: dd-${{ env.XCODE_VERSION }}-${{ env.FLUTTER_VERSION }}-${{ hashFiles('pubspec.lock', 'ios/Podfile.lock') }}
Signierung: codesign / Notarization.
Fallen: pod vor restore; kein save bei Fail — if: always().
8. Disk/Inode
Drei Ebenen → 60 % auf 256 GB; inode oft zuerst. Release-Sprint-Matrix.
du -sh ~/Library/Developer/Xcode/DerivedData ~/.cocoapods "${PUB_CACHE:-$HOME/.pub-cache}" 2>/dev/null
df -h .; df -i .
9. Abnahme
① pod <90s ② Hot −40 % ③ pub optional.
| Symptom | Ursache | Aktion |
|---|---|---|
| Hot≈Cold 20+ | DerivedData jedes Job | Pfad/restore |
| nur pod | CDN/Auth | CP_HOME_DIR, .netrc |
| Link-Fehler | Xcode-Mix | Key+Xcode |
| SSH hängt | Disk/inode | df + Governance |
10. FAQ
10.1 Warum so langsam auf Cloud Mac?
CocoaPods-Miss → Kette. Pods zuerst.
10.2 Pods oder DerivedData?
Pods zuerst.
10.3 Nur DerivedData?
Nein.
10.4 Runner-Cache?
§7; if: always().
10.5 Dual-Agent?
2026-06-03: worktree; hier CI-Cache.
11. Fazit
CocoaPods (🔥🔥🔥) → DerivedData (🔥🔥) → Flutter (🔥).
48h validieren
Nach Cloud Mac Dual-AI-Agent: Claude Code + Codex-Isolation (2026-06-03) Nacht-Pipeline auf gleichem Mac mieten.
Onboarding: Mac-mieten-Checkliste · Spezifikation · Tarife