VPSSpark Blog
← Zurück zum Tagebuch

Reichen zwei Cloud Macs für Flutter-Team-CI?

Server-Notizen · Cloud Mac CI #9 · 2026.06.08 · ca. 12 Min.

Häufig gesucht: Flutter CI macOS · Flutter iOS Build · Cloud Mac Self-hosted Runner · Flutter Team CI

Flutter-Entwickler debuggt eine App auf dem Mac, CI-Pipeline im Kontext
Bei Flutter-CI knickt es meist auf iOS/macOS ein—zwei Cloud Macs für fast und archive.

VPSSpark Cloud Mac CI Serie #9. Eine Frage aus DACH-Flutter-Teams kommt ständig: Kann ein Team mit 8–15 Leuten die CI mit zwei Cloud Macs stabil betreiben? Ja — wenn Android und iOS getrennt gedacht werden: Android-Builds bleiben auf dem GitHub-gehosteten ubuntu-latest, auf macOS laufen zwei Self-hosted Runner in den Pools macos-fast und macos-archive, und die harte Regel gilt: kein Archive auf PR. Im Folgenden: Lastmodell, Job-Schichten, Workflow-Snippet und Go-live-Checkliste. Warteschlangen-Diagnose und Runner-Anbindung finden Sie in Serie #2 sowie im Praxisbericht drei Cloud Macs für hohes iOS-CI-Volumen.

1 · Überraschende Erkenntnis: Der Flutter-CI-Engpass liegt nicht bei Dart

Die meisten roten Flutter-CI-Läufe scheitern nicht an langsamem flutter test, sondern daran, dass iOS-Jobs die macOS-Runner-Slots blockieren — weil in PRs fälschlich flutter build ipa mitläuft.

Cross-Platform suggeriert oft „ein Workflow für alles“. In der Praxis laufen flutter analyze und Unit-Tests auch auf Linux; macOS brauchen Sie für CocoaPods, Xcode-Compile, Signing und IPA-Export. Mischt ein Archive-Job in den PR-Pfad, blockiert ein Cloud Mac 25–40 Minuten — alle anderen PRs stehen auf Queued. Das kennen native iOS-Teams; die Diagnose bleibt wait time >> run time (siehe macOS-Runner-Warteschlangen-Diagnose).

Zwei Cloud Macs sind daher keine „zwei Universal-Maschinen“, sondern eine bewusst getrennte Zwei-Pool-Topologie: eine für schnelles Feedback, eine für Release-Schwere. APK/AAB bleiben auf Linux — macOS-Slots nicht für Gradle verbrennen.

2 · Lastmodell: Wie viel macOS-Parallelität braucht ein 8–15-Personen-Team?

Als Referenz ein mittelaktives Flutter-Team (1–2 PRs pro Person und Tag, nightly auf main, wöchentliche Releases):

12
Typische Teamgröße
~18
macOS-Jobs/Werktag
2
Cloud-Mac-Slots

Im Fast-Pool (macos-fast): analyze, Tests, iOS-Simulator-Build — Ziel 8–14 Minuten Wall Time mit pub-/DerivedData-Cache. Im Archive-Pool (macos-archive): Release-IPA plus Notarisierung — 20–35 Minuten, aber selten (main, Tags, nightly). Gemischte Pools lassen Archive den Fast-Pool verstopfen; getrennt bleibt die P95-Wartezeit für PRs meist unter 10 Minuten — ausreichend für Code-Review-Rhythmus in Berlin, München oder Zürich.

Ab 15+ Leuten oder vielen Matrix-Flavors im Monorepo lohnt eine dritte Archive-Redundanz — Kapazitätsplanung wie im 500-Builds/Tag-Szenario, nicht Gegenstand dieses Zwei-Maschinen-Starts.

Plattform Runner Typischer Job Im PR?
Android ubuntu-latest (gehostet) flutter build apk/appbundle, Android-Tests Ja
iOS Fast Feedback Cloud Mac #1 · macos-fast analyze, Tests, build ios --simulator Ja
iOS Release Cloud Mac #2 · macos-archive build ipa, Notarisierung, TestFlight Nein

3 · Drei Flutter-Job-Ebenen: CI Hard Rules

Flutter-Pipeline auf L0/L1/L2 mappen — passend zum On-call-Handbuch:

Ebene Flutter-Inhalt Pool PR-Trigger?
L0 dart format --set-exit-if-changed, flutter analyze, leichte Tests Linux oder macos-fast Ja
L1 Integrationstests, flutter build ios --simulator, Widget-Tests macos-fast Ja · kein Archive
L2 flutter build ipa, App Store Connect, Notarisierung macos-archive Nein · nur main/tag/schedule

Drei Regeln wie bei nativem iOS: Rule 1 PR ohne L2; Rule 2 L2 nur im isolierten Pool; Rule 3 Fast-Pool darf nicht durch Archive blockiert werden. Flutter-spezifisch: vieles in L0 läuft auf Linux — wer alles an macos-fast bindet, verschwendet macOS-Kapazität. PR: Linux- und macOS-Jobs parallel; macOS nur für „nicht auf Linux machbar“.

4 · Zwei-Maschinen-Topologie: Aufgaben von Cloud Mac #1 und #2

Abb. 1 · Typische Topologie: zwei Cloud Macs für Flutter-Team-CI

GitHub Actions · PR / main getrenntAndroid → ubuntu-latest
Cloud Mac #1 · macos-fastL0/L1 · 8–14 min/Job
Cloud Mac #2 · macos-archiveL2 · nur main/tag

Cloud Mac #1 (Fast-Pool): Labels [self-hosted, macOS, macos-fast, flutter]. Flutter SDK fixiert (fvm oder Image-Pin), Xcode, CocoaPods. Runner als Dauer-Service mit Autostart; pub cache und DerivedData aggressiv warm halten — kurze Jobs, hoher Durchsatz.

Cloud Mac #2 (Archive-Pool): Labels [self-hosted, macOS, macos-archive, flutter-release]. Physisch getrennt — nie zwei Pool-Runner auf einem Mac (sonst ist Rule 2 wirkungslos). Distribution-Zertifikate und App Store Connect API Key im Keychain; Anbindungsschritte analog Runner-Onboarding in der Praxis. Vor Release: flutter doctor -v und Dry-run-Archive — Fast-Pool nicht kontaminieren.

Gleiche Region, gleiche Xcode-Hauptversion — weniger „Fast grün, Archive rot“. Cloud Mac: festes Image, stabile GitHub-Anbindung, kein Büro-UPS — gut für 7×24-CI. Zur Sicherheitsgrenze Self-hosted Runner: Archive-Maschine auf ein Repo oder eine Org beschränken.

5 · Workflow-Beispiel: PR und main getrennt

Kern der Aufteilung (vollständige Pipeline braucht noch Cache, Secrets, Artifacts). Flutter-CD-Dokumentation zu iOS-Signing; Zertifikate im Archive-Keychain vorausgesetzt.

flutter-ci.yml (Kern)
jobs:
                  android-pr:
                    if: github.event_name == 'pull_request'
                    runs-on: ubuntu-latest
                    steps:
                      - run: flutter analyze && flutter test
                      - run: flutter build appbundle --release

                  ios-pr-fast:
                    if: github.event_name == 'pull_request'
                    runs-on: [self-hosted, macOS, macos-fast, flutter]
                    steps:
                      - run: flutter analyze
                      - run: flutter test
                      - run: flutter build ios --simulator --no-codesign

                  ios-release:
                    if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
                    runs-on: [self-hosted, macOS, macos-archive, flutter-release]
                    steps:
                      - run: flutter build ipa --export-options-plist=ExportOptions.plist
                      # TestFlight / Notarisierung — nur L2

In ios-pr-fast fehlt bewusst build ipa. Geräte-Build auf PR: nur per workflow_dispatch, weiterhin Archive-Pool — nicht als Standard-PR-Event. Mit concurrency in der Workflow-Syntax alte PR-Runs abbrechen — oft ~20 % weniger sinnlose Wartezeit.

6 · Cache-Checkliste: vier Verzeichnisse für Flutter-Teams

Zwei Cloud Macs laufen stabiler mit gutem Cache als mit der neuesten CPU. Im Runner-Home persistieren (oder nightly Snapshot):

  • PUB_CACHE / ~/.pub-cache — Dart-Deps; bei pubspec.lock-Wechsel inkrementell
  • Flutter-SDK-Verzeichnisfvm pinnt Version; kein flutter upgrade in CI
  • ios/Pods + CocoaPods-Cachepod install ist oft der iOS-Schwanz
  • Xcode DerivedData — hoher Nutzen bei gleichem ios/Podfile.lock

Fast- und Archive-Pool kein gemeinsames DerivedData — Debug/Simulator und Release/Archive vermischen erzeugt Linker-Geisterfehler. Gradle-Cache für Android auf ubuntu-latest via Actions Cache; Cloud-Mac-Disk schonen.

Image-Baseline
Monatlich „sauberes“ Image: Flutter minor + Xcode patch + pod repo update — zuerst auf Archive validieren, dann Fast-Pool. Tägliche PRs nur Cache; kein flutter pub upgrade als CI-Default.

7 · Go-live: von null auf Zwei-Maschinen-Anbindung

In 1–2 Werktagen PoC-fähig:

  • 2 gleich spezifizierte Cloud Macs (M4 + 16 GB empfohlen; iOS-Compile-Spitzen oft 12 GB+)
  • #1: Flutter/Xcode/CocoaPods, Runner macos-fast; #2: Signing-Material, Runner macos-archive
  • Workflows splitten: Android ubuntu-latest; PR nur L1; main/tag L2
  • pub/DerivedData/Pods persistieren; 3 Tage P95 wait < 10 min beobachten
  • Bleibt wait >> run: zuerst L2 in PRs prüfen, dann Maschinen addieren

PoC mit einer Cloud Mac für Fast-Pool; Archive vorübergehend auf gehostetem macos-latest (Wartezeit akzeptieren) — Logik validieren, dann zweite Maschine für Release-SLA. Widerspricht nicht „zwei Maschinen“: die zweite sichert Releases, nicht PR-Feedback.

8 · FAQ

Alle Flutter-Tests auf Linux — reicht ein Cloud Mac?

Ohne macOS-Compile im PR theoretisch ja — die meisten Teams fahren build ios --simulator für Native-Plugins. Ein Mac für L1 geht; Archive muss vom Fast-Feedback isoliert bleiben, sonst blockieren Releases alle PRs. Zwei Maschinen bleiben die sichere Wahl.

Zwei Cloud Macs vs. zwei Mac minis im Büro?

Topologie identisch; Unterschied im Betrieb: Cloud Mac ohne Hardware-Beschaffung, snapshotbare Images, oft stabilere GitHub-Bandbreite. Büro-Mac bei 7×24 und Serverraum; verteilte DACH-Teams wählen häufiger Cloud Mac — siehe auch Skalierung bei hohem Job-Volumen.

Codemagic oder gehostetes GitHub-macOS behalten?

Als Archive-Backup oder PR-Overflow. Nach Self-hosting-Hauptpfad lohnt gehostetes macOS bei < 4 Releases/Monat; aktive Flutter-Teams sparen langfristig oft mit eigenen Runnern.

Monorepo mit mehreren Apps — Job-Zählung?

Matrix × Flavors multiplizieren. Mehr als vier macOS-Jobs pro PR: Fast-Pool erweitern oder Matrix straffen — nicht Archive in PRs schieben.

Serie (#9 Flutter Zwei-Maschinen-CI)
#1 Kapazität · #2 Warteschlange · #3 Self-hosted Kosten · #8 Build-Tempo · #9 dieser Artikel

Zwei Cloud Macs — Flutter-CI für Android und iOS sauber getrennt

Android bleibt auf Linux; iOS-Compile, Signing und IPA-Export brauchen macOS. Zwei Cloud Mac mini M4 mit macos-fast und macos-archive halten PR-Feedback und Release-Jobs auseinander — günstiger als Archive in jeden PR und dann für alle Queued. Apple Silicon beschleunigt flutter build ios und Xcode-Link; ~4 W im Leerlauf passen zu dauerhaftem 7×24-CI.

Gegenüber Büro-Mac-Raum: Cloud Mac per Tarif, klonbare Images, stabile GitHub-Anbindung — ideal für verteilte Flutter-Teams in DACH. Unix-native für Flutter, CocoaPods, Fastlane ohne Extra-Virtualisierung.

Für die iOS-CI-Migration Ihres Flutter-Teams: Start mit zwei VPSSpark Cloud Mac mini M4 als Minimal-TopologieTarife ansehen, Fast-Pool-PoC, danach Archive-Pool für Releases.

Angebot

Flutter-CI hängt an iOS? Zwei Cloud Macs, zwei Pools

macos-fast + macos-archive · PR und Release trennen

Zur Startseite
Angebot Tarife ansehen