VPSSpark Blog
← Zurück zum Entwicklertagebuch

Mit 3 Cloud Macs tragen wir 500 iOS-CI-Builds pro Tag

Server-Notizen · 2026.06.02 · ca. 14 Min.

GitHub Actions Self-hosted Mac Runner-Warteschlange auf Cloud Mac

Kurz gesagt: Bei rund 500 iOS-CI-Builds pro Tag brauchen die meisten Teams keine acht Mac minis — drei Apple-Silicon-Cloud-Macs reichen, wenn Sie Jobs in GitHub Actions sauber schichten und Self-hosted Mac Runner in eine Fast- und eine Release-Warteschlange trennen, statt auf jeder Maschine parallel drei vollständige Archives laufen zu lassen.

Vor drei Monaten haben wir die Xcode-CI-Umgebung eines iOS-Teams übernommen. Ausgangslage:

  • 18 Entwicklerinnen und Entwickler
  • 2 Apps in einem gemeinsamen Monorepo
  • GitHub Actions als einzige CI-Plattform
  • Etwa 500 macOS-Jobs pro Tag (PR-Checks, Unit-Tests, Nacht-Releases)

Die Beschaffungsvorlage sah vor: acht Mac minis im Serverraum als Self-hosted Runner. Wir haben zwei Wochen Workflow-Logs nach Job-Typ aufgeschlüsselt — und die Realität wich stark von der Bauchgefühl-Rechnung ab: Etwa 70 % PR-Validierung und Integrations-Builds, 20 % Simulator-Unit-Tests, nur rund 10 % Archive plus Upload (TestFlight-Upload lässt sich separat buchen, siehe Abbildung 1).

Umsetzung: 2 × Fast Mac Runner (PR / Tests) + 1 × Release Mac Runner (Archive, Notarisierung, TestFlight) — drei Cloud Macs tragen das gesamte iOS CI/CD stabil. Im Folgenden: Kapazitätsrechnung, warum kein Xcode Cloud und kein vollständig gemanagtes Bitrise, sondern Self-hosted Runner auf dedizierten Knoten.

Abb. 1 · Tagesmix bei ~500 iOS-CI-Builds (zwei Wochen GitHub Actions, Messwerte des Teams)

PR Build
65%
Unit Test
20%
Archive
10%
Upload
5%
3
Cloud Macs (Praxis reicht)
8→3
Beschaffung reduziert
63
Maschinenstunden/Tag (mit Cache)

Kapazität rechnen: von 91 auf 63 Maschinenstunden

500 Jobs pro Tag über 24 Stunden sind im Mittel etwa 21 Jobs pro Stunde — entscheidend sind aber Spitzen: Vormittags-Merges in DACH und US-East, PR-Stürme vor Release-Tagen. Peaks liegen oft beim Drei- bis Fünffachen des Mittelwerts. Kapazität muss für Warteschlangen in Spitzenzeiten dimensioniert werden, nicht für den Mittelwert um Mitternacht.

Der zweite Hebel ist die Job-Dauer: Simulator-Tests dauern typisch 4–8 Minuten; PR-Builds mit Dependency-Auflösung 12–20 Minuten; Release-Archive inklusive Notarisierung und Upload oft 25–45 Minuten. Aus zwei Wochen P50 des Teams: leichte Jobs ~6 Minuten, schwere ~35 Minuten. Wer fälschlich 30 % Archive annimmt, kommt auf „acht Macs nötig“ — nach Aufschlüsselung sind schwere Jobs bei 500 Runs deutlich seltener als leichte.

Ohne Cache ergibt eine Grobrechnung (325 leicht + 175 schwer) etwa 91 Maschinenstunden pro Tag — mehr als drei Macs physikalisch liefern (72 Stunden). Mit DerivedData-/SPM-Cache, L2-Serialisierung und Queue-Labels sinken leichte Jobs auf ~4 Minuten, schwere im Warm-Pfad auf ~22 Minuten; der Bedarf fällt auf etwa 63 Maschinenstunden. Bei einem Peak-Faktor ~1,3 ist das tragbar — die Mathematik hinter „drei reichen“.

Abb. 3 · Täglicher macOS-Maschinenstunden-Bedarf (Modell: 500 Jobs/Tag)

Ohne Cache
91 Std.
Mit Cache
63 Std.

Xcode-CI schichten: Archive nicht in dieselbe Runner-Warteschlange wie PRs

Pipeline-Setups, die 500 Runs pro Tag tragen, nutzen fast immer drei Xcode-CI-Stufen:

  • L0: SwiftLint, Einzelmodul-Compile — Label macos-fast.
  • L1: PR-Integrationsbuild ohne Archive — ebenfalls Fast Mac Runner, pro Knoten bis zu 2 leichte Jobs parallel.
  • L2: Archive, Notarisierung, TestFlight — Label macos-archive, maximal 1 Job gleichzeitig pro Maschine.

GitLab CI, Buildkite und Jenkins folgen demselben Prinzip: Routing per Label, keine Sammelwarteschlange. Burst-Elastizität mit Buildkite: Buildkite Mac Agent an Cloud Mac. Einordnung Mac VPS vs. dedizierter Runner-Pool: Mac-VPS-Leitfaden.

Drei Cloud Macs: Rollen und Topologie

Die produktive statische Drei-Knoten-Topologie (Namen flexibel, Rollen bitte beibehalten):

Knoten Runner-Label Aufgabe Parallelität
Mac-A macos-fast PR Build, Unit Test 2 × L0/L1
Mac-B macos-fast Symmetrisch zu Mac-A, Fast-Queue 2 × L0/L1
Mac-C macos-archive Archive, Notarisierung, Upload 1 × L2

Abb. 2 · Drei-Knoten-Topologie (GitHub Actions Self-hosted Runner)

Mac-AFast Mac Runner
Mac-BFast Mac Runner
Fast QueuePR Build · Unit Test · L0/L1
Mac-CRelease Mac Runner
Archive QueueArchive · Notarisierung · TestFlight Upload
In Lasttests und Produktion setzen wir VPSSpark Cloud Mac mini M4 und M4 Pro ein (zwei Fast- + ein Release-Knoten). Kapazitätszahlen, Queue-P95 und Cache-Trefferquoten stammen aus Self-hosted Runner auf Apple-Silicon-Cloud Mac — keine Hackintosh- oder Grau-VM-Umgebungen.

Zwei Fast-Knoten liefern Durchsatz, ein Release-Knoten liefert planbare Auslieferung. Steht die L2-Warteschlange am Release-Tag über dem SLO, kann Mac-A temporär macos-archive tragen — dann L0-Parallelität abschalten, sonst konkurrieren Keychain und DerivedData-Locks und Signierung bricht sporadisch. Alle drei Xcode-Minor-Versionen müssen mit den Xcode Release Notes übereinstimmen.

Parallelität: 16-GB-M4 und GitHub Actions Mac Runner
Leichte Xcode-CI-Jobs: 2 Jobs pro Maschine; vollständiges Archive: 1 Job. Zwei parallele Archives auf 16 GB M4 sind der häufigste Grund für scheinbare „Unterdimensionierung“ bei 500 Runs/Tag.

Warum GitHub Actions Mac Runner schnell in die Warteschlange rutschen

Viele Teams starten mit GitHub-gehosteten macOS Runnern und stellen bei PR-Flut Wartezeiten fest. Selten liegt es an „GitHub ist langsam“, sondern an: (1) org-weitem macOS-Concurrency-Limit; (2) Workflows, die standardmäßig volle Archives pro PR fahren; (3) fehlender Trennung nach Job-Typ bei Self-hosted Mac Runner — schnelle Jobs hängen hinter langsamen. Vor dem Umzug auf drei dedizierte Cloud Macs lag queue_wait P95 bei gehosteten Runnern über 40 Minuten; mit Fast-/Archive-Doppelqueue sank L1-P95 unter 8 Minuten.

Praxis: drei Cloud Macs als Self-hosted-Baseline, gehostete Runner nur für Open-Source-Wochen oder extreme Spitzen auf L0 — Kosten kontrollierbar, Secrets müssen nicht wöchentlich migriert werden.

Self-hosted Runner vs. Xcode Cloud

Xcode Cloud passt zu Teams, die tief in Apples Ökosystem bleiben und null Agent-Betrieb wollen. Self-hosted Mac Runner passen, wenn Sie Warteschlangen, Cache-Keys und die Mischung mit internem Jenkins/Buildkite steuern müssen. Vergleich:

Dimension Xcode Cloud Cloud Mac + Self-hosted Runner
Abrechnung Minutenpaket + Concurrency-Cap Cloud-Mac-Abo/Tag, planbare Maschinenstunden
Warteschlange Plattformweit Eigene fast/archive-Labels
Secrets ASC-Integration bequem Match / Keychain — eigenes Runbook
Ideal für Seltene Releases, wenig Customizing 500+ Runs/Tag iOS CI/CD

Wann nach Minutenpaket-Engpass auf Tages-Cloud-Mac wechseln, steht in der FAQ Xcode Cloud Minutenpaket vs. Cloud Mac für Archive.

Warum dieses Team Xcode Cloud abgelehnt hat

Xcode Cloud wurde evaluiert; gewählt blieb GitHub Actions + Self-hosted Runner, weil: (1) Backend und Android bereits auf GitHub laufen — kein zweites CI; (2) eigene Cache-Keys und Monorepo-Matrix nötig; (3) in Release-Wochen Job-Volumen über dem Komfortbereich des Minutenpakets, Warteschlange nicht steuerbar. Xcode Cloud ist nicht schlecht — es passt schlecht zu „500 Runs/Tag, Queue unter Kontrolle“.

Bitrise vs. Self-hosted Mac Runner — Kostenintuition

Bitrise und ähnliche Mobile-DevOps-SaaS sparen Agent-Ops, berechnen aber Concurrency-Tarife. Für 18 Personen, zwei Apps, ~500 Jobs/Tag liegen Jahreskosten oft über einem Abo für drei Cloud Macs, und Archive-Parallelität bleibt am Tarif gebunden. Bitrise eignet sich für Startups ohne Runner-Lust; wer zwei Wochen in Self-hosted Mac Runner investiert, amortisiert Knoten oft in 3–6 Monaten. Bereits auf Bitrise? L2 schrittweise auf einen Release-Knoten — kein Big-Bang nötig.

Buildkite Mac Agent: Stärken und Grenzen

Buildkite hält die Queue in der Cloud, Agenten auf Ihrer Hardware — gute Burst-Elastizität, klare Artifact-Aufbewahrung. Nachteil: zusätzliche Orchestrierung; für drei Macs wirkt es manchmal oversized. Der Kunde hat Buildkite PoC’d: Burst top, dennoch GitHub Actions nativ, weil YAML-Kompetenz nur dort sitzt. Mit bestehendem Buildkite gilt dieselbe fast/archive-Label-Strategie wie oben — Details im Buildkite-Cloud-Mac-Artikel.

Cloud Mac vs. lokale Mac-mini-CI

Acht Mac minis im eigenen Rack: hohe CapEx, Abschreibung, Stromausfall, Zertifizierungs-Audit. Drei Cloud Macs: planbare OpEx, Tages-PoC, Region nahe Git. Lokale CI lohnt bei >14 Stunden Compile/Tag pro Maschine über Jahre konstant. Cloud Mac CI passt zu schwankenden Peaks, Contractor-Wochen, Review-Saisons mit Notarisierungs-Engpass. Das Team behält zwei Büro-Macs für Entwicklung; schwere Xcode-CI läuft in der Cloud — statt acht minis, die meist idle sind.

Queue-SLO: vierte Maschine nur mit Daten, nicht Bauchgefühl

Empfohlene Metriken: queue_wait_seconds (P95), run_duration nach L0/L1/L2, cache_hit_ratio, l2_concurrent (selten > 3). Beispiel-Schwellen: L1 queue P95 < 8 Min.; L2 P95 < 25 Min. Erst wenn L2 drei Tage hintereinander über Schwellwert liegt und Cache-Treffer > 60 %, fourth Release-Knoten prüfen.

Caching: der Hebel für 63 Maschinenstunden

DerivedData-Key: branch + Xcode-Version; SPM/CocoaPods-Lock-Änderung bustet den Cache. Fast-Knoten teilen read-only Cache, Release-Knoten hält L2-DerivedData lokal auf NVMe. Signing-Material über Vault — nicht im Cache-Bundle. Keys müssen macOS/Xcode-Minor enthalten, sonst „Treffer, aber Link-Fehler“ nach Upgrade.

Minimale GitHub Actions Self-hosted Runner-Konfiguration

Drei Registrierungen: macos-fast ×2, macos-archive ×1. L2 braucht concurrency, damit Release-Jobs sich nicht gegenseitig canceln:

GitHub Actions · Release Mac Runner
concurrency:
                  group: ios-archive-${{ github.ref }}
                  cancel-in-progress: false
                jobs:
                  archive:
                    runs-on: [self-hosted, macos-archive]
                    steps:
                      - uses: actions/checkout@v4
                      - run: xcodebuild archive -scheme App -archivePath build/App.xcarchive

Release-Knoten: dedizierter macOS-User + Match; nach Reboot Unlock-Skript vor der Nacht-Queue. Details: xcodebuild-Dokumentation und Fastlane.

Release-Tag: wenn 500 Runs zu 650 werden

Reihenfolge: (1) nicht-kritisches L0 stoppen; (2) gehostete Runner nur für L1; (3) vierten Cloud Mac 48 Stunden tageweise dazu. L2 dauerhaft auf 2 parallel pro Maschine erhöhen zahlt man mit zufälligen Notarisierungs-Fehlschlägen.

Wann die vierte Maschine Pflicht wird

  • L2 queue P95 eine Woche > 40 Min., Cache bereits optimiert.
  • Monorepo mit >5 Apps an einem Release-Knoten, Nachtfenster reicht nicht.
  • Jeder PR mit vollem Archive — zuerst Pipeline, nicht Hardware kaufen.

Anti-Patterns

Volles Archive pro PR; Mac Runner ohne Label-Trennung; zwei Archives auf einem 16-GB-M4; Cache-Keys ohne Branch; nur Success-Rate statt queue_wait — alles lässt drei Maschinen „zu wenig“ wirken und treibt falsche Acht-Mini-Bestellungen.

FAQ: kurze Antworten für Suche und Snippets

Wie viele Macs für 500 iOS-Builds pro Tag?

Mit Job-Schichtung (PR / Tests / Archive getrennt) und DerivedData-Cache reichen für die meisten Teams drei Apple-Silicon Cloud Macs: 2 × Fast Mac Runner + 1 × Release Mac Runner. Ist über die Hälfte der Runs volles Archive, Pipeline anpassen oder vierten Release-Knoten planen.

Wie viele Jobs parallel auf GitHub Actions Mac Runner?

Auf 16-GB-M4: 2 leichte Jobs (PR, Unit Test) oder 1 Archive-Job. Dauerhaft „2× Archive“ vermeiden.

Warum Archive nicht hoch parallel?

Speicherdruck → Swap, Disk-Queue, Keychain-Locks und codesign-Konflikte — sporadische Timeouts statt reproduzierbarer Compile-Fehler.

Ist Cloud Mac günstiger als Xcode Cloud?

Bei hochfrequentem iOS CI/CD, dauerhaftem Self-hosted Runner und residenten Secrets sind drei Cloud Macs meist planbarer als Minuten-Abrechnung. Seltene Releases, null Ops: Xcode Cloud zuerst testen.

Bitrise oder drei Cloud Macs?

Bitrise spart Ops, eignet sich für schnellen Start. Bei ~500 Jobs/Tag und eigener Queue-/Cache-Kontrolle gewinnt oft Self-hosted Mac Runner + Cloud Mac — niedrigere Jahreskosten, L2-Concurrency nach Ihren Regeln.

VPSSpark: Cloud-Mac-Baseline 2 Fast + 1 Release

Wer „500 Runs/Tag — wie viele Macs?“ rechnet: zwei Wochen Logs wie Abb. 1 aufschlüsseln, dann prüfen, ob 63 Maschinenstunden für Ihr Modell passen. VPSSpark Cloud Mac mini M4 / M4 Pro — Tages-PoC oder Abo — für GitHub Actions Self-hosted Mac Runner, PR und Archive in getrennten Queues.

Siehe Mac-Cloud-Tarife oder VPSSpark-Startseite — Region wählen, echten Workflow bucketen, dann entscheiden ob drei reichen — statt acht minis vorab zu bestellen.

Limitiert

500 iOS-CI-Jobs/Tag: drei Cloud Macs reichen

GitHub Actions Mac Runner · Self-hosted · fast/archive

Startseite
Limitiert Tarife ansehen