VPSSpark Blog
← Retour au journal dev

3 Cloud Mac ont tenu 500 builds iOS CI par jour

Notes serveur · 2026.06.02 · ~14 min

File d'attente CI iOS et Mac Runner self-hosted sur Cloud Mac

Conclusion d’entrée : ~500 builds iOS CI par jour — la plupart des équipes n’ont pas besoin de huit Mac mini ; trois Cloud Mac Apple Silicon suffisent, à condition de stratifier les jobs dans GitHub Actions et d’isoler les files rapide et release via des Self-hosted Mac Runner, plutôt que de lancer trois archives complètes en parallèle sur chaque machine.

Il y a trois mois, nous avons repris l’environnement Xcode CI d’une équipe iOS. Situation initiale :

  • 18 développeurs
  • 2 apps (monorepo partagé)
  • GitHub Actions comme seule CI
  • Environ 500 jobs macOS/jour (PR, tests unitaires, releases nocturnes)

Le cahier des charges prévoyait huit Mac mini en salle pour des runners self-hosted. Deux semaines de logs découpés par type de job ont montré un écart net avec l’intuition : ~70 % validation PR et builds d’intégration, 20 % tests Simulator, ~10 % archive + upload (l’upload TestFlight peut être comptabilisé à part, voir figure 1).

Déploiement retenu : 2 × Fast Mac Runner (PR / tests) + 1 × Release Mac Runner (archive, notarisation, TestFlight) — trois Cloud Mac portent tout le iOS CI/CD. Ci-dessous : calcul de capacité, pourquoi pas Xcode Cloud ni Bitrise entièrement managé, et pourquoi des Self-hosted Runner sur nœuds dédiés.

Fig. 1 · Composition d’une journée ~500 builds iOS CI (mesure GitHub Actions sur 2 semaines)

PR Build
65%
Unit Test
20%
Archive
10%
Upload
5%
3
Cloud Mac (suffisant en prod)
8→3
Réduction du plan d’achat
63
heures-machine/jour (cache activé)

Calcul de capacité : de 91 à 63 heures-machine

500 jobs/jour sur 24 h ≈ 21 jobs/h en moyenne — mais ce sont les pics qui comptent : merges matinaux Europe/Amérique, tempête de PR avant release. Les pics atteignent souvent 3 à 5× la moyenne. Dimensionnez pour la file aux pics, pas pour la moyenne de minuit.

Deuxième variable : durée par job. Tests Simulator : 4–8 min ; build PR avec résolution de deps : 12–20 min ; archive release + notarisation + upload : 25–45 min. P50 sur deux semaines : jobs légers ~6 min, lourds ~35 min. En supposant à tort 30 % d’archives, on conclut « il faut huit Mac » — une fois ventilé, les jobs lourds sont minoritaires sur 500 runs.

Sans cache, estimation 325 légers + 175 lourds ≈ 91 heures-machine/jour, au-delà du plafond physique de trois Mac (72 h). Avec cache DerivedData/SPM, sérialisation L2 et labels de file, jobs légers ~4 min, lourds ~22 min en chemin chaud : besoin ≈ 63 heures-machine. Avec facteur de pic ~1,3, c’est tenable — la base mathématique du « trois suffisent ».

Fig. 3 · Besoin quotidien en heures-machine macOS (modèle 500 jobs/jour)

Sans cache
91 h
Avec cache
63 h

Stratifier Xcode CI : ne pas mélanger archive et PR dans la même file runner

Les pipelines qui tiennent 500 runs/jour utilisent presque toujours trois niveaux Xcode CI :

  • L0 : SwiftLint, compile module — label macos-fast.
  • L1 : build PR sans archive — Fast Mac Runner, jusqu’à 2 jobs légers en parallèle par nœud.
  • L2 : archive, notarisation, TestFlight — label macos-archive, 1 job max simultané par machine.

GitLab CI, Buildkite, Jenkins : même logique de labels, pas une file fourre-tout. Élasticité burst Buildkite : agent Mac Buildkite sur Cloud Mac. Mac VPS vs pool runner : guide Mac VPS / macOS dans le cloud.

Trois Cloud Mac : répartition et topologie

Topologie statique à trois nœuds déployée (noms libres, rôles à conserver) :

Nœud Label runner Rôle Parallélisme
Mac-A macos-fast PR Build, Unit Test 2 × L0/L1
Mac-B macos-fast Symétrique à Mac-A, file rapide 2 × L0/L1
Mac-C macos-archive Archive, notarisation, Upload 1 × L2

Fig. 2 · Topologie trois nœuds (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 · Notarisation · TestFlight Upload
Nos tests de charge et la prod utilisent des nœuds VPSSpark Cloud Mac mini M4 et M4 Pro (deux fast + un release). Capacité, P95 de file et taux de hit cache proviennent de Self-hosted Runner sur Cloud Mac Apple Silicon — pas de Hackintosh ni VM grise.

Deux nœuds fast = débit ; un release = livraison prévisible. Si la file L2 dépasse le SLO un jour de release, Mac-A peut temporairement porter macos-archive — couper la parallélité L0, sinon Keychain et verrous DerivedData provoquent des échecs de signature aléatoires. Les trois minors Xcode doivent être alignés sur les notes de version Xcode.

Parallélisme recommandé : M4 16 Go et GitHub Actions Mac Runner
Xcode CI léger : 2 jobs par machine ; archive complète : 1 job. Deux archives parallèles sur un M4 16 Go expliquent souvent une « fausse saturation » à 500 runs/jour.

Pourquoi les GitHub Actions Mac Runner filent vite

Beaucoup commencent avec les runners macOS hébergés par GitHub et voient des files dès que les PR affluent. Rarement « GitHub est lent », plutôt : (1) plafond de concurrence macOS org-wide ; (2) workflows qui archivent tout par PR ; (3) pas de séparation par type de job sur Self-hosted Mac Runner — les jobs rapides derrière les lents. Avant trois Cloud Mac dédiés, queue_wait P95 > 40 min ; avec double file fast/archive, L1 P95 < 8 min.

Approche courante : trois Cloud Mac en pool self-hosted baseline, runners hébergés uniquement pour semaines open source ou pics extrêmes sur L0 — coût maîtrisé, secrets sans migration hebdo.

Self-hosted Runner vs Xcode Cloud

Xcode Cloud convient aux équipes ancrées Apple, zéro ops agent. Self-hosted Mac Runner convient si vous pilotez files, clés de cache et mélange avec Jenkins/Buildkite interne. Tableau :

Axe Xcode Cloud Cloud Mac + Self-hosted Runner
Facturation Forfait minutes + plafond concurrence Abonnement/jour Cloud Mac, heures-machine maîtrisées
File Plateforme unique Labels fast/archive maison
Secrets Intégration ASC simple Match / Keychain — runbook interne
Idéal pour Releases rares, peu de custom 500+ runs/jour iOS CI/CD

Signaux de bascule après saturation du forfait minutes : FAQ plafonds Xcode Cloud vs Cloud Mac pour archive.

Pourquoi cette équipe a écarté Xcode Cloud

Xcode Cloud a été évalué ; choix final GitHub Actions + Self-hosted Runner : (1) backend et Android déjà sur GitHub — une seule CI ; (2) clés de cache et matrice monorepo sur mesure ; (3) semaines release au-delà du confort du forfait minutes, file non pilotable. Xcode Cloud n’est pas mauvais — il est mal aligné avec « 500 runs/jour, file sous contrôle ».

Bitrise vs Self-hosted Mac Runner — intuition coût

Bitrise et SaaS mobile DevOps épargnent l’ops agent, facturent la concurrence. Pour 18 personnes, deux apps, ~500 jobs/jour, le coût annuel dépasse souvent l’abonnement trois Cloud Mac, avec archive toujours plafonnée par le tier. Bitrise pour les startups sans envie de runner ; deux semaines d’investissement Self-hosted Mac Runner → amortissement 3–6 mois. Déjà sur Bitrise ? migrer L2 vers un nœud release, pas tout d’un coup.

Agent Mac Buildkite : avantages et limites

Buildkite : queue cloud, agents sur votre matériel — bon burst, artifacts clairs. Inconvénient : couche d’orchestration ; parfois excessif pour trois Mac. PoC client excellent sur burst, GitHub Actions natif retenu (YAML connu). Avec Buildkite existant, même stratégie labels fast/archive — voir l’article Buildkite Cloud Mac.

Cloud Mac vs CI Mac mini locale

Huit Mac mini en salle : CapEx, amortissement, coupure, audit datacenter. Trois Cloud Mac : OpEx prévisible, PoC à la journée, région proche du Git. CI locale si compile >14 h/jour/machine stable sur des années. Cloud Mac CI pour pics variables, semaines prestataires, saison review + notarisation. L’équipe garde deux Mac bureau pour le dev ; Xcode CI lourd dans le cloud — plutôt que huit minis idle.

SLO de file : agrandir avec des données, pas au feeling

Métriques : queue_wait_seconds (P95), run_duration par L0/L1/L2, cache_hit_ratio, l2_concurrent (rarement > 3). Seuils exemple : L1 P95 < 8 min ; L2 P95 < 25 min. Quatrième nœud release seulement si L2 dépasse trois jours d’affilée et cache > 60 %.

Cache : levier des 63 heures-machine

DerivedData : clé branch + version Xcode ; changement lock SPM/CocoaPods bust. Nœuds fast partagent cache read-only ; release stocke DerivedData L2 en local NVMe. Matériel de signature via vault — hors bundle cache. Clés avec minor macOS/Xcode, sinon « hit mais link fail » post-upgrade.

Configuration minimale GitHub Actions Self-hosted Runner

Trois enregistrements : macos-fast ×2, macos-archive ×1. L2 exige concurrency pour éviter cancel croisé :

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

Nœud release : user macOS dédié + Match ; script unlock après reboot avant file nocturne. Réf. documentation xcodebuild et Fastlane.

Jour de release : 500 runs deviennent 650

Ordre : (1) stopper L0 non critique ; (2) runners hébergés pour L1 seulement ; (3) quatrième Cloud Mac 48 h à la journée. Monter L2 à 2 parallèles/machine durablement → échecs notarisation aléatoires.

Quand la quatrième machine devient obligatoire

  • L2 queue P95 > 40 min une semaine, cache déjà optimisé.
  • Monorepo >5 apps sur un release, fenêtre nuit insuffisante.
  • Archive complète par PR — corriger la pipeline avant d’acheter.

Anti-patterns

Archive complète par PR ; runners sans labels ; deux archives sur un M4 16 Go ; clés cache sans branche ; succès seul sans queue_wait — tout fait croire que trois machines « ne suffisent pas » et pousse à huit minis.

FAQ : réponses courtes pour la recherche

Combien de Mac pour 500 builds iOS/jour ?

Avec stratification (PR / tests / archive) et cache DerivedData, trois Cloud Mac Apple Silicon suffisent souvent : 2 Fast Mac Runner + 1 Release Mac Runner. Si >50 % des runs sont archive complète, ajuster la pipeline ou planifier un quatrième nœud release.

Combien de jobs en parallèle sur GitHub Actions Mac Runner ?

Sur M4 16 Go : 2 jobs légers (PR, unit test) ou 1 job archive. Éviter « 2× archive » en continu.

Pourquoi ne pas paralléliser fortement les archives ?

Pression mémoire → swap, file disque, verrous Keychain et conflits codesign — timeouts sporadiques, pas erreurs de compile reproductibles.

Cloud Mac moins cher que Xcode Cloud ?

Pour iOS CI/CD haute fréquence, Self-hosted Runner permanent et secrets résidents, trois Cloud Mac sont souvent plus prévisibles que la minute. Releases rares, zéro ops : tester Xcode Cloud d’abord.

Bitrise ou trois Cloud Mac ?

Bitrise épargne l’ops, idéal démarrage rapide. ~500 jobs/jour avec contrôle file/cache : Self-hosted Mac Runner + Cloud Mac gagne souvent — coût annuel plus bas, concurrence L2 à vos règles.

VPSSpark : baseline Cloud Mac 2 Fast + 1 Release

Vous calculez « 500 runs/jour — combien de Mac ? » : deux semaines de logs comme fig. 1, puis valider si 63 heures-machine tiennent pour votre modèle. VPSSpark Cloud Mac mini M4 / M4 Pro — PoC journalier ou abonnement — pour GitHub Actions Self-hosted Mac Runner, PR et archive en files séparées.

Voir les offres Mac cloud ou l’accueil VPSSpark — choisir une région, bucketiser un workflow réel, puis décider si trois suffisent — plutôt que commander huit minis d’emblée.

Offre limitée

500 jobs iOS CI/jour : trois Cloud Mac suffisent

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

Accueil
Offre limitée Voir les plans