Faire tourner un runner self-hosted CircleCI sur un Mac
CircleCI machine runner 3 s’installe sur macOS depuis le tap Homebrew de CircleCI et tourne comme LaunchAgent. Créez un namespace et une resource class, copiez le token, mettez-le dans le config.yaml du runner, puis lancez le service. Les jobs l’atteignent avec machine: true et resource_class: namespace/name.
Ce qu’il vous faut d’abord
- Les droits d’administrateur de l’organisation dans CircleCI. Un administrateur doit accepter les conditions des runners sous Org, puis Runners, avant que le menu apparaisse.
- Au moins un crédit sur le compte. CircleCI indique que les jobs des runners ne consomment pas de crédits, mais le stockage et le transfert réseau peuvent en consommer.
- Un Mac Apple silicon avec accès administrateur, Homebrew et Xcode.
sha256sum, que CircleCI liste comme prérequis. Obtenez-le avecbrew install coreutils.
1. Créez un namespace et une resource class
Dans l’app web, ouvrez Runners et sélectionnez Create Resource Class. Chaque organisation a un seul namespace. Si vous publiez des orbs, vous l’avez déjà. Donnez à la resource class un nom comme mac-mini-m6. Enregistrez, et copiez le token. CircleCI ne l’affiche qu’une fois.
La CLI fait la même chose :
circleci namespace create <name> --org-id <your-organization-id> circleci runner resource-class create <namespace>/<resource-class> <description> --generate-token
2. Installez le runner avec Homebrew
brew tap circleci-public/circleci brew trust circleci-public/circleci brew install circleci-runner
La ligne du milieu est nouvelle. Homebrew 7.0.7, la version présente sur notre Mac en octobre 2026, refuse de charger des paquets d’un tap tiers tant que vous ne lui faites pas confiance. La page de CircleCI ne montre pas encore cette étape. Le runner arrive sous forme de cask Homebrew.
macOS peut afficher une notification indiquant qu’un élément d’arrière-plan de Circle Internet Services a été ajouté. C’est normal. Homebrew écrit aussi le plist du LaunchAgent dans ~/Library/LaunchAgents/com.circleci.runner.plist.
3. Ajoutez le token dans config.yaml
nano $HOME/Library/Preferences/com.circleci.runner/config.yaml
runner: name: "mac-mini-m6" working_directory: "/Users/$USER/Library/com.circleci.runner/workdir" cleanup_working_directory: true api: auth_token: "your-resource-class-token"
Avec cleanup_working_directory activé, chaque job part d’un checkout propre. Le DerivedData de Xcode vit par défaut dans ~/Library/Developer/Xcode/DerivedData, donc les caches de build survivent quand même. Si vous passez -derivedDataPath dans le dossier de travail, le nettoyage le supprime après chaque job.
Protégez le token
Le token de la resource class permet à une machine de réclamer les jobs de cette classe. Quiconque le lit peut brancher sa propre machine et recevoir vos jobs, avec vos secrets. Réservez le fichier de config à votre utilisateur, et changez le token s’il fuit un jour.
chmod 600 $HOME/Library/Preferences/com.circleci.runner/config.yaml ls -l $HOME/Library/Preferences/com.circleci.runner/config.yaml
La même logique vaut pour les jobs. Ils tournent sous l’utilisateur macOS qui a démarré le runner, sur le même disque que tout le reste. Ne dirigez vers cette resource class que des projets de confiance.
4. Acceptez la notarisation
Le binaire vient d’internet, donc macOS doit l’approuver. CircleCI documente d’abord la vérification de la signature, puis le retrait du drapeau de quarantaine.
spctl -a -vvv -t install "$(brew --prefix)/bin/circleci-runner" sudo xattr -r -d com.apple.quarantine "$(brew --prefix)/bin/circleci-runner"
La première commande doit répondre accepted, avec la source Notarized Developer ID.
5. Démarrez le runner dans le domaine GUI
Lancez ces commandes depuis le Terminal sur le bureau du Mac. Le domaine GUI, c’est la session de bureau ouverte. C’est là que vivent le simulateur et le trousseau de session.
launchctl bootstrap gui/$(id -u) $HOME/Library/LaunchAgents/com.circleci.runner.plist launchctl enable gui/$(id -u)/com.circleci.runner launchctl kickstart -k gui/$(id -u)/com.circleci.runner launchctl print gui/$(id -u)/com.circleci.runner
CircleCI documente aussi une option de domaine utilisateur pour les sessions headless. Elle déplace le plist dans /Library/LaunchAgents. Sur macOS 27, nous avons vu un plist dans ce dossier casser la connexion automatique à chaque démarrage. Voir la connexion automatique sur un Mac headless. Pour le travail iOS, nous conseillons le domaine GUI avec la connexion automatique.
6. Faites-le survivre à un redémarrage
Un plist dans ~/Library/LaunchAgents se charge quand son utilisateur ouvre sa session. Activez la connexion automatique pour ce compte dans Réglages Système, sous Utilisateurs et groupes. Désactivez la veille avec sudo pmset -a sleep 0. Redémarrez une fois et vérifiez de nouveau launchctl print. Les logs sont dans ~/Library/Logs/com.circleci.runner/runner.log.
7. Dirigez un job vers le Mac
version: 2.1
jobs:
ios-tests:
machine: true
resource_class: your-namespace/mac-mini-m6
steps:
- checkout
- run:
name: Run tests
command: |
rm -rf build/TestResults.xcresult
xcodebuild test -project MyApp.xcodeproj -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.5' -resultBundlePath build/TestResults.xcresult
- run:
name: Zip result bundle
when: always
command: ditto -c -k --keepParent build/TestResults.xcresult TestResults.xcresult.zip
- store_artifacts:
path: TestResults.xcresult.zip
workflows:
ios:
jobs:
- ios-testsLa documentation de CircleCI nomme deux champs obligatoires pour un job de runner : machine: true et la resource_class. Il n’y a pas de clé macos: xcode: ici, car vous ne choisissez pas d’image. Le job utilise le Xcode sélectionné sur le Mac. Sur un Mac MacRun, c’est Xcode 26.6 avec le runtime du simulateur iOS 26.5.
Erreurs courantes et solutions
Refusing to load cask ... from untrusted tap. Lancezbrew trust circleci-public/circleci, puis réinstallez.- macOS bloque le binaire. Vous avez sauté l’étape de notarisation. Lancez la commande
xattrci-dessus. - Les jobs restent en file sans jamais démarrer. Vérifiez que la resource class dans config.yml correspond à celle que vous avez créée, puis lisez
runner.log. - Le cache des couches Docker ne fonctionne pas. CircleCI l’indique comme non pris en charge sur les runners self-hosted.
xcodebuild: error: Existing file at -resultBundlePath. Supprimez l’ancien bundle avant l’étape de test. Nous avons eu ce problème sur Xcode 26.6.
Pour arrêter le runner, utilisez launchctl bootout gui/$(id -u)/com.circleci.runner. Pour le supprimer, lancez brew uninstall --cask circleci-public/homebrew-circleci/circleci-runner.
Pourquoi un Mac dédié aide
Les caches chauds sont le principal gain. Dans notre benchmark, nous avons compilé l’app iOS Wikipedia avec Xcode 26.6. Un job typique après un petit changement a pris 27 secondes sur un M6 déjà chaud. Il a pris 269 secondes sur un runner macos-26 neuf hébergé par GitHub. Un build propre a pris 86 secondes contre 183.
Quand macOS hébergé par CircleCI suffit
CircleCI propose ses propres executors Mac. Sa documentation, lue en octobre 2026, liste m4pro.medium avec 6 vCPU et 28 Go, et m4pro.large avec 12 vCPU et 56 Go. Les deux ont plus de mémoire que notre M6 de 16 Go. Si votre suite a besoin d’autant de mémoire, ou si vous compilez quelques fois par semaine, restez sur l’hébergé. Évitez aussi MacRun s’il vous faut un SLA, une IP fixe ou plusieurs régions.
Consultez les autres systèmes de CI pour le côté MacRun de l’installation, ou comparez les coûts avec vos propres minutes.
Questions fréquentes
Le runner self-hosted CircleCI est-il gratuit ?
+
CircleCI indique que l’exécution sur un runner ne consomme pas de crédits. Il faut au moins un crédit sur le compte, car le stockage et le transfert réseau peuvent encore être facturés.
Où se trouve la config du runner CircleCI sur macOS ?
+
Dans $HOME/Library/Preferences/com.circleci.runner/config.yaml. Les logs vont dans $HOME/Library/Logs/com.circleci.runner/runner.log.
Faut-il utiliser le domaine GUI ou le domaine utilisateur ?
+
Pour les builds iOS, le domaine GUI avec la connexion automatique. Il tourne dans la session de bureau dont le simulateur et le trousseau de session ont besoin.
Le cache des couches Docker fonctionne-t-il sur un runner self-hosted CircleCI ?
+
Non. CircleCI indique que le cache des couches Docker n’est pas pris en charge sur les runners self-hosted.