クラウド時代の逆張り:GitHub Actions を手元のM1 Maxに移して年19万円+月30時間を取り戻す
「クラウドCIにすべて任せる」が常識化した今、あえて手元のM1 Maxにビルドを戻したらどうなるか を実費ベースで試算した。
- 3つのFlutterリポジトリのGitHub Actions使用状況を調査
- 実請求CSVから集計すると 直近30日で $120.65(約18,940円) だった
- うち macOSビルドが76%($92.13) を占める
- M1 Max を self-hosted runner にすれば iOS + Android の両方をローカル化 でき、年間 約19万円のコスト削減
- ビルド時間は 16〜18分 → 5〜7分(約1/3) に短縮、月30時間の待ち時間削減
- 専用機(Mac mini M2 Pro 約20万円)を購入しても ROI 約1年 で十分採算が合う
- 時間価値換算では 年約273万円の生産性向上ポテンシャル
背景
Flutter製モバイルアプリを2つ運営しており、それぞれが共通モジュール(submodule)を参照する構成。CI/CDはGitHub Actionsで回しているが、月間費用がいくらかかっているか、内訳まできちんと把握できていなかった。
そこで以下を整理した:
- 各リポジトリのワークフロー一覧
- 直近30日の実行回数・時間
- 実費用の内訳(公開料金ではなく実請求ベース)
- self-hosted runner への移行による削減ポテンシャル
- ビルド時間短縮による開発体験への影響
1. ワークフロー一覧の取得
gh CLI を使ってワークフローと実行履歴を取得する。
# ワークフロー一覧
gh api repos/<org>/<repo>/actions/workflows \
--jq '.workflows[] | {id, name, state, path}'
# 直近の実行履歴(過去30日)
gh api "repos/<org>/<repo>/actions/runs?created=>=2026-04-03&per_page=100" \
--paginate \
--jq '.workflow_runs[] | [.name, .conclusion, .run_started_at, .updated_at] | @tsv'
2. 直近30日の実行統計
3リポジトリ合計で 609 runs / 5,702 分 の実行があった。
Repository A(メインアプリ)
| ワークフロー | runs | 失敗率 |
|---|---|---|
| Release Orchestrator | 132 | 23% |
| CI Smoke Check | 176 | 53% |
| Android Dev Build | 1 | - |
| Dependabot | 4 | 75% |
Repository B(姉妹アプリ)
| ワークフロー | runs | 失敗率 |
|---|---|---|
| Release Orchestrator | 97 | 28% |
| CI Smoke Check | 119 | 4% |
| create-asana-attachment | 30 | 3% |
| その他 | 50 | - |
Repository C(共通モジュール)
| ワークフロー | runs |
|---|---|
| submodule-pr-trigger | - |
| parent-build-result | - |
| develop-parent-sync | - |
3. ジョブレベルでのOS別集計
Release Orchestrator は中で iOS(macOS)と Android(Linux)のreusable workflowを呼び出すため、ジョブの labels を見て分類する必要がある。
gh api "repos/<org>/<repo>/actions/runs/<run_id>/jobs" \
--jq '.jobs[] | [.labels[0], .name, .started_at, .completed_at] | @tsv'
実行例の出力:
ubuntu-latest bump 2026-05-02T15:12:12Z 2026-05-02T15:12:24Z
macos-15 ios/build-ios 2026-05-02T15:12:29Z 2026-05-02T15:29:09Z
ubuntu-latest android/build 2026-05-02T15:12:27Z 2026-05-02T15:32:09Z
4. macos-15 の実体を確認する
macos-15 ラベルが Apple Silicon か Intel か、ジョブのログから判定できる。
job_id=<id>
gh api "repos/<org>/<repo>/actions/jobs/$job_id/logs" | grep -E "Image:|architecture:"
出力:
Image: macos-15-arm64
architecture: ARM64
[command]/opt/homebrew/bin/git ... # Apple Silicon の Homebrew パス
→ macos-15 = Apple Silicon 3-core(料金は$0.08/min)
ラベル別の構成は以下:
| ラベル | 構成 | 公開料金 |
|---|---|---|
macos-15 |
Apple Silicon 3-core | $0.08/min |
macos-15-large |
Intel 12-core | $0.16/min |
macos-15-xlarge |
Apple Silicon 6-core | $0.32/min |
5. 実費用の取得(Usage Report CSV)
GitHub UI から CSV をダウンロードする。
https://github.com/organizations/<org>/settings/billing/usage
→ 「Get usage report」ボタン
→ 期間選択(直近30/90/180日)
→ メールで CSV のリンクが届く
CSV のカラム:
date, product, sku, quantity, unit_type,
applied_cost_per_quantity, gross_amount, discount_amount, net_amount,
username, organization, repository, workflow_path, cost_center_name
awk で集計:
awk -F',' '$12=="\"<repo>\"" {
gsub(/"/,"",$3); gsub(/"/,"",$4); gsub(/"/,"",$7); gsub(/"/,"",$13);
totalq[$13"|"$3] += $4;
totalgross[$13"|"$3] += $7;
}
END {
for (k in totalq) {
split(k, p, "|");
printf "%-50s %-25s %10.1f %10.4f\n", p[1], p[2], totalq[k], totalgross[k];
}
}' usageReport.csv
6. 実費用の集計結果
単価(CSV applied_cost_per_quantity より)
公開料金より約25%安いレートが適用されていた(Enterprise契約割引と推測)。
| SKU | 公開料金 | 実レート |
|---|---|---|
actions_linux (2-core) |
$0.008/min | $0.006/min |
actions_linux_4_core |
$0.016/min | $0.012/min |
actions_macos |
$0.08/min | $0.062/min |
リポジトリ別合計(直近30日)
| リポジトリ | 実行分 | gross | net |
|---|---|---|---|
| Repository A | 3,372 | $66.99 | $64.71 |
| Repository B | 1,970 | $51.50 | $50.78 |
| Repository C | 360 | $2.16 | $2.10 |
| 合計 | 5,702 | $120.65 | $117.59 |
SKU別合計
| SKU | 分 | gross | 構成比 |
|---|---|---|---|
| actions_macos | 1,486 | $92.13 | 76% |
| actions_linux (2-core) | 3,678 | $22.07 | 18% |
| actions_linux_4_core | 538 | $6.45 | 5% |
7. M1 Max への移行検討
macos-15 ランナーは Apple Silicon 3-core
つまり手元の M1 Max は GitHub の macos-15 とアーキテクチャ互換。self-hosted runner として登録すれば、iOS ビルドをローカルで動かせる。
Android ビルドも実は M1 Max でそのまま動く
最初は「Linux ジョブはDocker経由が必要」と考えたが、Android ビルドに限って言えばそれは過剰。Apple Silicon の macOS で完全ネイティブに動作する。
| 構成要素 | M1 Max での動作 |
|---|---|
| Gradle | JVMベースでOS非依存 |
| Android SDK / NDK / build-tools | Google公式で macOS-arm64 版を配布 |
| Kotlin / Java コンパイル | ARM64ネイティブ |
| AGP(Android Gradle Plugin) | macOS対応 |
| Firebase CLI / fastlane | macOSで日常使い |
| APK/AAB 署名 | apksigner / jarsigner どちらも macOS で動作 |
runs-on: ubuntu-latest を runs-on: [self-hosted, macOS, ARM64] に書き換えるだけで動く。
移行可能性の判定
| カテゴリ | 説明 | M1 Max での実現方法 |
|---|---|---|
| 🟢 そのまま移行可 | macOS native + Android | Apple Silicon ARM64 でネイティブ実行 |
| 🟡 工夫して移行可 | 純Linux ジョブ | Docker / Lima / UTM 経由 |
| 🔴 移行不可 | GitHub-managed | Dependabot等は対象外 |
| 区分 | 分 | 費用 | 全体比 |
|---|---|---|---|
| 🟢 そのまま移行可(iOS + Android) | 3,364 | $106.62 | 88% |
| 🟡 工夫すれば移行可(Linux軽量ジョブ) | 2,330 | $13.97 | 12% |
| 🔴 移行不可 | 8 | $0.05 | 0% |
推奨戦略:iOS + Android を移行
| 項目 | 値 |
|---|---|
| 移行対象 | 2リポジトリの iOS + Android ビルド |
| カバー時間 | 3,364分(全体の59%) |
| 削減費用 | $106.62/月 |
| ビルド時間短縮 | 16〜18分 → 5〜7分(約1/3) |
| 技術難度 | 低(Xcode / Android SDK 既存資産を流用) |
| 維持コスト | 低(Xcode/Flutter/Android SDK 更新のみ) |
CI Smoke Check や Asana attachment のような短時間頻発ジョブはGitHub-hostedで放置するのが最適。並列実行リソースを self-hosted で奪い合わずに済む。
8. ROI計算
前提
| 項目 | 値 |
|---|---|
| 為替レート | $1 = 157円(2026-05-03 時点) |
| 電気代単価 | 36円/kWh |
| M1 Max 平均消費電力 | アクティブ時 100W / アイドル時 30W |
| 月次削減額 | $106.62(約16,740円) |
電気代の試算
iOS + Android ビルドの合計アクティブ稼働時間は 約53.8時間/月。残りはアイドル(Sleep禁止)。
| 状態 | 時間 | 消費電力 | kWh |
|---|---|---|---|
| アクティブ | 53.8h | 100W | 5.38 |
| アイドル(Sleep禁止) | 666.2h | 30W | 19.99 |
| 月合計 | 720h | - | 25.37 kWh |
電気代: 25.37 × 36円 = 913円/月(約 $5.82)
年間正味削減
| 項目 | 月額 | 年額 |
|---|---|---|
| GitHub Actions 削減 | -$106.62(-16,740円) | -$1,279(-200,880円) |
| 電気代 | +$5.82(+913円) | +$70(+10,956円) |
| 正味削減 | -$100.80(-15,827円) | -$1,209(-189,924円) |
シナリオ別比較(5年スパン、運用工数は業務吸収)
| シナリオ | 機材投資 | 5年累計効果 |
|---|---|---|
| A. 既存M1 Max共用 | 0円 | +95万円 |
| B. Mac mini M2 Pro 専用機 | 20万円 | +75万円 |
| C. Mac Studio M2 Max 専用機 | 30万円 | +65万円 |
投資回収期間
ROI = 投資額 / 年間削減額
Mac mini M2 Pro: 20万円 / 19.0万円 = 1.05年
Mac Studio M2 Max: 30万円 / 19.0万円 = 1.58年
専用機を買っても 約1年でペイ。さらに5年使えば 65〜95万円の純益。
9. ビルド時間短縮 — もう一つの大きなメリット
費用削減だけが移行のメリットではない。ビルド時間の短縮による開発体験の改善こそ最大の効果かもしれない。
スペック比較
| 項目 | GitHub-hosted macos-15
|
GitHub-hosted ubuntu-4core
|
M1 Max (16-inch MacBook Pro) |
|---|---|---|---|
| CPU | Apple M1, 3 perf cores | Linux 4 cores | Apple M1 Max, 10 cores (8P+2E) |
| GPU | 8コア | - | 24 or 32コア |
| メモリ帯域 | ~68 GB/s | ~25 GB/s | 400 GB/s (約6〜16倍) |
| RAM | 7GB(実効) | 16GB | 32 〜 64GB |
| ストレージ | ~14GB SSD | ~14GB SSD | NVMe SSD(数TB) |
| キャッシュ | ジョブごとに毎回ダウンロード | 同上 | 永続キャッシュ可 |
実測ビルド時間(直近30日の平均)
CSV と jobs API から逆算した1ジョブあたりの所要時間:
| ビルド種別 | runner | 1ジョブ平均 | M1 Max移行後(推定) |
|---|---|---|---|
| iOS(Repository A) | macos-15 | 12.6分 | 5〜7分 |
| iOS(Repository B) | macos-15 | 12.6分 | 5〜7分 |
| Android(Repository A) | ubuntu-2core | 6.7分 | 2〜3分 |
| Android(Repository B) | ubuntu-4core | 9.7分 | 3〜5分 |
| Release Orchestrator全体(並列実行、最遅律速) | - | 16〜18分 | 5〜7分 |
なぜここまで速くなるのか
-
コア数が3倍以上(macOS 3-core or Linux 4-core → 10-core)
- Flutter の Dart kernel ビルド、Xcode の Swift コンパイル、Gradle の Kotlin コンパイルはマルチスレッド処理が中心
- メモリ帯域も6〜16倍あるためボトルネックが解消
-
キャッシュが永続化される
-
~/.pub-cache,~/Library/Developer/Xcode/DerivedData,~/.gradle/caches,Pods/がジョブをまたいで残る - GitHub-hosted では毎回
actions/cacheでダウンロード(300MB〜1GB)が必要だが、self-hosted では不要
-
-
Gradle daemon が常駐
- Android ビルドでは Gradle daemon の起動だけで30秒〜1分かかる
- self-hosted ならプロセスが常駐するので即時実行
-
ネットワークレイテンシゼロ
- submodule clone, gem install, pod install のダウンロードがローカルキャッシュ or 高速回線
-
キューイング待機ゼロ
- GitHub-hosted は混雑時にジョブがキューに数分待つことがある
- self-hosted は専有なのでトリガー即実行
開発体験への影響
Before(GitHub-hosted、16〜18分/リリース)
PR push → CI開始まで30秒〜2分のキュー待ち
→ ビルド開始
→ 16〜18分待機(コーヒー1杯)
→ TestFlight + Firebase App Distribution upload完了
1日のリリース確認サイクル: 約20分
集中力の中断: 数回
After(M1 Max、5〜7分/リリース)
PR push → CI即開始(キューなし)
→ 5〜7分でビルド完了
→ upload完了
1日のリリース確認サイクル: 約8分
集中力の中断: ほぼなし(フロー維持可能)
月間の時間削減効果
| 項目 | GitHub-hosted | M1 Max | 削減 |
|---|---|---|---|
| iOS ビルド月間累計 | 24.8時間 | 11.5時間 | -13.3h |
| Android ビルド月間累計 | 29.0時間 | 12.0時間 | -17.0h |
| 合計 | 53.8時間 | 23.5時間 | -30.3h/月 |
エンジニアの実働時間として、月30時間以上の待ち時間が削減される。チーム全員で共有されるなら、その効果は人数倍に広がる。
副次効果
① デプロイ頻度の向上
- ビルドが速いと「ちょっと修正してTestFlight投げる」のハードルが下がる
- 結果として 1日あたりのリリース回数が増え、フィードバックサイクルが短縮
② Flaky テストの再実行コストが低い
- ビルドが Flaky で再実行になっても、5分なら待てる
- GitHub-hosted の16分待ちだと「もう放置でいいか」となりがち → 品質低下
③ デバッグ効率
- ローカル M1 Max なので、ビルドが失敗したら
~/_work/配下に直接アクセスしてログ調査可能 - GitHub Actions の Web UI でログを追うより圧倒的に早い
④ 並列化のポテンシャル
- M1 Max は 10コアあるため、複数ジョブを並列実行可能
- iOS と Android を1台で同時にビルドしても余裕
金銭価値換算
エンジニア時給を仮に 7,500円($50相当)とすると:
| 項目 | 値 |
|---|---|
| 月間削減時間 | 30.3時間 |
| 時間価値換算 | 7,500円 × 30.3 = 227,250円/月 |
| 年間 | 2,727,000円(約 $17,370) |
費用削減(年 約19万円)よりもむしろ 時間価値の方が14倍以上大きい。
10. 注意点
セキュリティ
- private repo でもメンバーは workflow_dispatch などで任意コードを実行可能
- 個人マシン共用だと secrets が個人環境に展開される
- → 専用ユーザー or 専用マシンでの分離が望ましい
運用負荷
- Xcode / Flutter / Android SDK のバージョン更新は手動
- DerivedData / pub_cache / Pod cache / Gradle cache の定期掃除が必要
- マシン故障時の単一障害点リスク
- Sleep禁止設定 + launchd 常駐化が必要
ディスク
- 最低150GB、できれば200GB以上の空きを確保
- iOS ビルド1回あたり 5〜10GB、Android ビルドは1〜3GB 生成される
Linux 固有の依存があれば書き換え必要
Android ビルドのワークフロー内に以下が含まれていれば調整が必要:
-
apt-get install→brew installに置き換え - bash スクリプトの GNU 拡張(
sed -iの挙動差など) - Docker container action(macOS 上の Docker Desktop で動かすか、別アプローチ検討)
実際のプロジェクトでは Linux 固有処理は最小限で、ほぼ書き換え不要だった。
まとめ
- GitHub Actions の費用を 実請求CSVベース で正確に把握すれば、削減ポイントは明確
- macOS ビルドが圧倒的にコスト支配項(10倍単価)
- M1 Max を self-hosted runner にすれば iOS と Android の両方 がローカル化可能
- 既存マシン共用ならノーリスクで 年19万円のコスト削減
- 専用機購入でも ROI 約1年で十分採算が合う
- 更にビルド時間が16〜18分 → 5〜7分に短縮し、エンジニアの月30時間が浮く
- 金銭価値換算では 時間短縮効果 (年約273万円) > 費用削減 (年約19万円)
クラウドCI全盛の時代に、あえてローカルに戻すという選択肢。Apple Silicon ネイティブ世代のMacの性能を考えると、これは「時代遅れ」ではなくむしろ 理にかなった逆張り だ。クラウドの利便性は維持しつつ、重い iOS / Android ビルドだけ手元に降ろすハイブリッド構成が、コスト・時間・開発体験のすべてを改善する現実解。
Discussion