Was zuerst wichtig ist
- Nicht nur xcodebuild ist langsam; entscheidend ist der Unterschied zwischen Cache- und Maschinen-Lebenszyklus.
- GitHub-hosted Jobs starten kalt, ein Self-hosted Runner bleibt wie eine warme Arbeitsmaschine.
- Persistente DerivedData- und CocoaPods-Caches bringen meist den größten Zeitgewinn.
- Disk- und Signing-Overhead wird oft fälschlich als Compilerproblem gelesen.
- Für Flutter iOS ist „Mac mieten“ für 48h Profiling sinnvoll, danach Self-hosted monatlich.
Lokaler Mac vs GitHub Actions: wo die Zusatzminuten entstehen
Lokal vergleichen viele Teams unter diesen Bedingungen:
- Lokal: persistente SSD + warmer Toolchain-State; CI: ephemeres VM-Image + kalte Abhängigkeiten.
- Lokal: bestehender Signing-Kontext; CI: neuer Keychain-Init pro Lauf.
- Lokal: wiederholte Builds auf demselben Host; CI: mehr Branch-bedingte Cache-Misses.
In der CI-Pipeline sieht es typischerweise so aus:
- Jeder Lauf startet mit einem sauberen Workspace.
flutter build ipaoderxcodebuild archiveläuft in Release.- Auf gehosteten Runnern bleiben Caches selten warm — effektiv Cold Start.
Viele Teams vergleichen nur `xcodebuild archive`. In der Realität summieren sich Checkout, pod install, Keychain-Setup und DerivedData-Invalidierung. Genau daraus entsteht oft die 2-3x Wahrnehmung.
Fünf Gründe, warum xcodebuild in CI 2-3x langsamer wird
Diese Muster sehen wir regelmäßig in Flutter iOS Pipelines auf GitHub Actions und bei Migrationen auf Self-hosted Runner.
- 1) Zustandslose Runner resetten alle Dependency-Layer GitHub-hosted VMs werden pro Job neu aufgebaut. SPM-Checkout, Pods-Effekte und Modulcache bleiben nicht stabil warm.
- 2) Variable DerivedData-Pfade zerstören Inkrementalität Bei wechselnden Pfaden kann xcodebuild vorhandene Artefakte nicht sauber wiederverwenden und kompiliert unnötig neu.
- 3) Flutter-Plugin-Auflösung triggert kaltes Pod-Verhalten Bei lockfile-Drift kostet `pod install` schnell mehrere Minuten. Deterministische Dependency-Regeln sind hier zentral.
- 4) Signing- und Keychain-Schritte addieren serielle Latenz Zertifikatsimport, Keychain-Erstellung und Entsperren passieren in CI wiederholt und kosten fix Zeit.
- 5) Disk- und Inode-Druck bremst Schreibpfade Nicht nur CPU zählt. Wenig freier SSD-Platz und Inode-Engpässe verlangsamen Indexing und Build-Outputs deutlich.
Stage-Timing-Tabelle (Flutter iOS Beispiel)
Nutzt dieses Format für mindestens zehn Läufe, bevor ihr Provider oder Architektur wechselt.
| Stage | Lokaler Mac | GitHub Actions | Typische Ursache |
|---|---|---|---|
| flutter pub get | 0m25s | 1m10s | pub Cache nicht warm |
| pod install | 1m30s | 4m45s | kalte Specs + Plugin-Auflösung |
| xcodebuild compile/archive | 6m40s | 12m20s | DerivedData-Mismatch |
| codesign/export | 0m55s | 2m00s | Keychain-Bootstrap je Lauf |
| gesamt | 9m30s | 20m15s | ca. 2.1x langsamer |
Bevor ihr xcodebuild beschuldigt
Bevor Sie xcodebuild optimieren, prüfen Sie pod install. CocoaPods ist in CI meist aus drei Gründen langsam:
Podfile.locknicht committed — Auflösung wird unvorhersehbar.- Kein Cache für
ios/Pods— Artefakte verschwinden nach dem Job. - Zusätzliches
pod repo update— mit Lockfile meist unnötig.
Nutzen Sie in CI pod install --deployment und halten Sie Pods auf demselben SSD-Pfad. Self-hosted Mac mini spart Upload/Download von GitHub-Caches.
DerivedData, Pods und Cache: praxistaugliche Architektur
In Flutter iOS liefert ein stabiler absoluter DerivedData-Pfad plus konsistente Pod-Caches oft den größten Hebel. Mit Self-hosted Runner wird CI von wegwerfbar zu dauerhaft optimierbar.
Auch auf GitHub-hosted kann deterministische Pfad- und Lockfile-Politik helfen. Wenn möglich, bringt ein gemieteter Apple-Silicon-Host als Self-hosted Runner meist lokalähnliches Warmverhalten.
- Fester derivedDataPath pro Target statt temporärer Pfade.
- `pod install --deployment` gegen Lockfile-Drift.
- Freie Disk + Inode als feste CI-Gesundheitsmetriken.
Fix-Muster: stabile Pfade + deterministische Dependency-Restore
Der folgende Ausschnitt ist bewusst robust statt trickreich. Ziel ist reproduzierbares Verhalten in Teams.
concurrencyim Workflow — alte Builds derselben Branch abbrechen.- Tests auf Linux — keine Mac-Minuten für
flutter test. - Pods + DerivedData persistieren — feste Pfade self-hosted, feine Cache-Keys hosted.
- Nur den Shipping-Flavor bauen — nicht sechs Flavors scannen.
- Xcode/Flutter-Version pinnen — kein
upgradein CI.
# .github/workflows/ios-ci.yml
jobs:
ios:
runs-on: [self-hosted, macOS, ARM64, flutter-ios]
steps:
- uses: actions/checkout@v4
- name: Restore caches
run: |
mkdir -p "$HOME/Library/Caches/CocoaPods"
mkdir -p "$HOME/.pub-cache"
- name: Build with stable DerivedData path
run: |
flutter pub get
cd ios
xcodebuild -workspace Runner.xcworkspace -scheme Runner -configuration Release -destination 'generic/platform=iOS' -derivedDataPath "$HOME/ci-derived/runner" CODE_SIGNING_ALLOWED=NO
- name: Keep pod install deterministic
run: |
cd ios
pod install --deployment
Damit sind oft 30-50% Zeitgewinn möglich. Danach kann ein dedizierter Self-hosted Runner-Pool weitere Stabilität bringen.
Pre-Merge-Checklist gegen Performance-Regressionen
Nach Flutter- oder Xcode-Updates sollte diese Liste Standard sein.
- ☐ CI baut Release/IPA, lokal vergleichen Sie Debug
- ☐ Job führt
flutter cleanoder löscht DerivedData - ☐
pod installzeigt jedes Mal viele Installing-Zeilen - ☐ GitHub-hosted Runner ohne lokale SSD-Caches
- ☐ 16-GB-Maschine mit mehreren Jobs oder Simulatoren
- ☐ README-Änderung triggert vollen iOS-Build
- ☐ Xcode-Version in CI weicht vom lokalen Mac ab
Erste drei Punkte: Caches fixen. Letzte zwei: Trigger und Maschinengröße. Bei langsamer werdenden Builds auch Speicher prüfen.
FAQ
Ist GitHub Actions immer langsamer als lokal?
Nicht immer, aber bei Flutter iOS häufig, wenn Caches nicht warm bleiben. Self-hosted Runner schließen diese Lücke oft deutlich.
Direkt auf Self-hosted wechseln?
Erst messen. Wenn Kaltstartkosten dominieren, eine Pipeline auf Self-hosted spiegeln und Median aus zehn Läufen vergleichen.
Braucht man dafür teure Hardware?
Meist nicht. „Mac mieten“ auf Apple Silicon mit persistenten Caches reicht oft für spürbare Verbesserungen.
Wie überzeugt man das Team?
Stage-Zeiten in Wartekosten übersetzen und zusätzlich Release-Vorhersagbarkeit als Business-Wert zeigen.
Weiterführende Cluster-Links
Wenn ihr Flutter iOS CI neu aufsetzt, lest diese Seiten in der Reihenfolge.
- Hub: Flutter iOS CI Architektur und Entscheidungsbaum
- Cache-Strategie: DerivedData, CocoaPods, SPM
- Self-hosted Runner auf Cloud Mac für GitHub Actions
- iOS Code Signing in CI ohne Keychain-Chaos
- Disk- und Inode-Monitoring für Langläufer-Runner
Stabile iOS CI Geschwindigkeit auf echter Apple-Silicon-Hardware
kvmboot Cloud Mac bietet dedizierte M-Serien Hosts für Flutter iOS. Erst Mac mieten (täglich) zum Profiling, dann monatlicher Self-hosted Runner.
Wenn ihr CI-Varianz gegen planbare Releases tauschen wollt, Cloud-Mac-Tarife ansehen