← 技術ブログに戻る

GitHub Actions iOS CI が Mac で遅い理由 — 本当の加速は self-hosted runner

GitHub Actions iOS CI:Mac mini self-hosted runner と永続 DerivedData
ホステッド macOS は毎回コールドスタート。専用 Mac mini なら DerivedData が job 間で残る。

GitHub Actions で iOS CI を回していて「Mac なのに遅い」を感じると、つい CPU や Xcode バージョンに目が行きます。でも実際に時間を食っているのは、queue 待ち・毎回の冷スタート・キャッシュ不在です。だから今回の結論はシンプルで、本当の加速ポイントは self-hosted runner。専有 Mac mini の永続ディスクを使えるだけで、同じリポジトリでも体感が変わります。

この記事では、GitHub Actions の hosted macOS と専有 Mac mini self-hosted を同じ物差しで比較し、なぜ典型ケースで 約 28 分 → 約 9 分まで縮むのかを分解します(複数案件の中央値サンプルであり、保証値ではありません)。TCO は Runner のリージョン別比較、Flutter 実装は Flutter iOS CI、Xcode そのものの詰まりは Xcode ビルド遅延ガイド で補完できます。背景としては AI 計算基盤の競争 も同じ構図です。

1) 「Mac なのに遅い」の正体

まず誤解を外します。遅い原因の第一候補は「M シリーズが非力」ではなく、ephemeral 実行モデルです。hosted macOS は job ごとに環境が消えるので、pod install・SPM 解決・DerivedData が毎回ほぼ初期化されます。これが iOS CI の「毎回リセット」感の正体です。

先に確認: ログで queuedbootstrap の時間が長いなら、まず runner モデルを見直すのが近道です。YAML の細かいチューニングはその後でも間に合います。

2) 28 分はどこで消えるのか(4 分割)

体感でなく、4 区間に分けると議論が速くなります。

区間 hosted macOS の目安 主因
queued 3-12 分 共有プール待ち、macOS 並列枠の競合
bootstrap 6-10 分 Pods/SPM の再取得、冷キャッシュ
build/archive 8-14 分 DerivedData 不在で全量寄りに
sign/upload 3-6 分 署名素材ロード、ASC 送信待ち

ポイントは、コンパイルだけが犯人ではないこと。queue と bootstrap を削るだけでも、総時間は大きく落ちます。

3) 本当の加速ポイントは self-hosted runner

self-hosted の価値は CPU ではなく「同じ Mac・同じ SSD・同じユーザーで繰り返せること」です。つまりキャッシュの再利用が前提になります。仕組み自体は GitHub 公式ドキュメントの通りです。

GitHub Actions iOS CI: self-hosted 構成

Developer push / PR
lint/test は Linux へ
GitHub Actions scheduler
runs-on: [self-hosted, macos, ios-ci]
Dedicated Mac mini M4 runner
queue 最小化 + 永続キャッシュ
Archive -> TestFlight

job 間で残すと効くキャッシュ

  • DerivedData
  • ~/Library/Caches/CocoaPods
  • SourcePackages/.build
  • 署名用 keychain

4) Before / After(中央値サンプル)

同一規模の中規模 iOS リポジトリで見た、典型的な差分です。

区間 hosted macOS Mac mini self-hosted
queued7 分0 分
bootstrap9 分2 分
build/archive11 分4 分
sign/upload5 分3 分
合計約 28 分約 9 分

繰り返しになりますが、28→9 は保証ではなくサンプル中央値です。ただ、改善方向が一致しやすいのは「queue 消失 + キャッシュ定着」が効くためです。

5) キャッシュ設計(速さの再現性を作る)

self-hosted を入れても、毎回キャッシュを消すと hosted と同じに戻ります。運用ルールは次の 3 つだけで十分です。

  • DerivedData を固定し、通常ジョブで全削除しない。
  • CocoaPods キャッシュ~/Library/Caches/CocoaPods)を永続化する。
  • SPM キャッシュSourcePackages / .build)を runner ローカルに残す。

Xcode 側の基礎情報は Apple の Xcode ドキュメント を参照しつつ、実測で「2 回目以降がどれだけ短くなるか」を追うのが確実です。

6) workflow 実装例(最小)

iOS job を self-hosted に明示的に寄せます。PR 用と release 用は分離がおすすめです。

name: ios-release
on:
  push:
    branches: [main]
jobs:
  archive:
    runs-on: [self-hosted, macos, ios-ci]
    concurrency:
      group: ios-release-${{ github.ref }}
      cancel-in-progress: false
    steps:
      - uses: actions/checkout@v4
      - name: Build Archive
        run: xcodebuild -scheme MyApp -configuration Release archive
運用のコツ: release 系は concurrency を絞って、署名・DerivedData の競合を避けると失敗率も下がります。

7) どのタイミングで専有 Mac が得か

状況 hosted 継続 self-hosted 推奨
月 1-2 回だけ iOS リリース 急がなくて良い
週 1 回以上リリース 待ち時間が効いてくる 導入メリット大
PR と release が同時間帯で衝突 queue 増大 専有ラベルで分離
Flutter + native 混在 冷スタート負荷が高い 永続キャッシュで安定

費用判断は Runner TCO 比較 と合わせると、稟議がかなり通しやすくなります。料金確認は pricing からどうぞ。

8) 48 時間で判断するチェックリスト

  1. self-hosted runner を登録し、ios-ci ラベルを固定。
  2. 同一リポジトリで 3 回連続実行(cold 1 回 + warm 2 回)。
  3. 4 区間(queue/bootstrap/build/sign)を全回で記録。
  4. 2 回目以降で bootstrap/build が下がるか確認。
  5. 下がらない場合は「キャッシュが残っているか」を先に調査。

この手順で効果が見えれば、次の増強(2 台目、リージョン最適化)に進みやすくなります。

9) FAQ(よくある 8 問)

Q1. GitHub Actions の iOS CI が遅いのは Mac の性能不足?

多くは違います。queue と冷キャッシュが主因で、CPU 交換の前に runner モデルを見直す方が効きます。

Q2. self-hosted runner は安全?

private repo 前提なら運用可能です。fork 由来 PR を無条件実行しない、Secrets を分離する、SSH 鍵を定期更新する、の 3 点を守ってください。

Q3. 28 分から 9 分は必ず再現できる?

保証はできません。記事内の値は中央値サンプルです。小規模リポジトリや既存キャッシュが強い環境では差が小さくなります。

Q4. hosted と self-hosted を混在できる?

できます。Linux は hosted、iOS archive だけ self-hosted という分担が一番管理しやすいです。

Q5. Flutter プロジェクトでも同じ考え方?

同じです。flutter build ipa でも Pods/DerivedData の扱いが速度を決めます。詳細は Flutter 記事 を参照してください。

Q6. Xcode 側の遅延調整はどこを見る?

まずは build 設定と依存を見直します。詰まったときは Xcode ビルド遅延ガイド が近道です。

Q7. いきなり月額契約すべき?

まず 48 時間の実測が無難です。改善が確認できたら月額に切り替える方が失敗しません。

Q8. チーム全体最適としては何を見るべき?

CI 時間だけでなく、待ち時間による開発者拘束を含めた TCO を見るのがおすすめです。背景判断には AI 基盤記事 も参考になります。

結論

GitHub Actions iOS CI が Mac で遅いとき、まず疑うべきは「Mac の型番」ではなく「実行モデル」です。self-hosted runner + 永続キャッシュを入れると、queue と冷スタートの両方を同時に削れます。

最短ルートは、専有 Mac mini で 48 時間 A/B 実測し、効果が出たら本番フローへ展開すること。料金や導入計画は pricing を起点に整理するのが現実的です。

料金を見る