← 返回技術部落格

為什麼 GitHub Actions iOS CI 在 Mac 上這麼慢?self-hosted runner 才是真正的加速點

GitHub Actions iOS CI:Mac mini self-hosted runner 與持久 DerivedData
託管 macOS Runner 每次冷啟動;獨享 Mac mini 讓 DerivedData 跨 job 存活。

先說結論:GitHub Actions 跑 iOS CI 會慢,通常不是你專案太重,而是你把最吃機器、最吃快取的 Xcode 鏈路交給了「每次都像新機」的環境。這篇會用白話拆給你看:為什麼 Mac 上的 iOS CI 常常卡在 pod installxcodebuild archive,以及為什麼 self-hosted runner 才是實際能把速度穩下來的做法。延伸閱讀可先看 Runner 節點 TCOFlutter iOS CI 快取Xcode 卡頓解法,以及 算力基建趨勢

1)為什麼 GitHub Actions 跑 iOS CI 會慢?

iOS CI 的瓶頸不在 YAML,而是在 macOS build chain:簽名、Pods、DerivedData、Xcode toolchain 全都要在 Mac 上完成。GitHub 託管 runner 每次重建環境,快取命中率有限,流程自然回到「每次都接近冷啟動」。你會看到同一條 pipeline 今天 14 分鐘、明天 28 分鐘,抖動很大。

一句話版本:託管 runner 解決「能跑」,self-hosted runner 解決「跑得穩又快」。

2)時間到底花在哪?(iOS CI 慢點分解)

階段常見耗時為何在託管環境特別慢
pod install4–12 分Pods/Specs 快取不連續,常重新抓依賴
xcodebuild archive8–20 分DerivedData 不持久,編譯優勢難累積
Signing / Export2–8 分鑰匙圈與憑證初始化重複做
Upload TestFlight2–10 分網路路由與佇列不固定,時間波動大

3)實務可用架構:Linux 測試 + Mac 自管 Runner

GitHub Actions iOS CI 建議拓樸

Developer Push / PR
GitHub Actions (ubuntu-latest)
lint · unit test · Android
Self-hosted macOS Runner (Mac mini M4)
pod install → xcodebuild archive → export
TestFlight / App Store Connect

Runner 上要保留的三層快取

  • CocoaPods cache + Pods
  • DerivedData
  • Ruby/Bundler 與工具鏈

4)self-hosted runner 要怎麼設才會快?

  1. 固定機器與固定路徑:不要每次換工作目錄,讓快取真的命中。
  2. 鎖版本:Xcode、CocoaPods、Ruby 版本固定,減少「今天過、明天炸」的漂移。
  3. 限制並發:同一台 Mac 建議 1–2 條 iOS job,避免互搶 I/O。
  4. 把重更新動作抽離:例如 pod repo update 改成每日/每週維護 job,不要每次 release 都跑。
  5. 監控磁碟與快取大小:DerivedData 爆掉時,速度會直接崩盤。

官方文件可搭配看:GitHub 的 self-hosted runner 說明 與 Apple 的 Xcode release notes

5)Before / After:自管 Runner 的速度差異

環境首次建置(冷)第二次(熱快取)穩定度
GitHub 託管 macOS16–24 分14–20 分中,波動較大
自管 Mac mini M4 Runner14–22 分5–9 分,可預期
看重點:真正拉開差距的是「第二次之後」。只要你每週穩定發版,累積節省的等待時間非常可觀。

6)Workflow 範例(可直接改成你專案)

name: release-ios
on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  ios:
    runs-on: [self-hosted, macos, ios]
    steps:
      - uses: actions/checkout@v4
      - name: Install deps
        run: |
          bundle install
          cd ios && pod install && cd ..
      - name: Build archive
        run: xcodebuild -workspace App.xcworkspace -scheme App -configuration Release archive
      - name: Export IPA
        run: xcodebuild -exportArchive -archivePath build/App.xcarchive -exportOptionsPlist ios/ExportOptions.plist -exportPath build

另外,若你是 Flutter 團隊,思路完全一樣:用 Linux 跑測試,把 iOS 打包交給 Mac runner。細節可對照 這篇 Flutter 實作

7)落地清單:速度之外,還要顧穩定

  • 簽名資產:憑證與描述檔集中管理,避免人手機器差異。
  • 權限分離:Runner 用專用系統帳號,減少手動操作污染。
  • 重試策略:只重試網路步驟,不要整條 pipeline 無腦重跑。
  • TCO 盤點:硬體、託管、維運時間一起算,別只看月費。

若你正在算成本,可直接接著看 TCO 拆解Mac mini 定價

8)什麼時候該從託管改成 self-hosted?

團隊情境建議理由
每月 iOS 發版 ≤ 1 次先用託管導入成本不一定回本
每月 2–4 次,且常等 CI試行 1 台 self-hosted可快速驗證速度收益
每週固定發版 / 多 App直接上自管時間與穩定性收益最大
跨區團隊協作選離主倉最近區域減少下載與上傳延遲

如果你最近也在關注算力資源競爭,這個趨勢脈絡可參考 AI 算力基建戰

9)FAQ(8 題,實務口語版)

Q1:我只有一個 App,真的需要 self-hosted 嗎?
如果你一個月才發一次,先不用急;但只要團隊開始抱怨「每次都在等 iOS CI」,就該評估了。

Q2:最先優化哪一步最有感?
先把 pod install 和 DerivedData 快取做對,通常立刻有感。

Q3:self-hosted 會不會很難維護?
一開始要花半天到一天打底,但之後日常維護其實可控,重點是版本固定。

Q4:跟 Xcode 本地慢有關嗎?
高度相關。你本地慢的點,CI 只會放大。可一起看 Xcode 優化

Q5:一定要接 Fastlane 嗎?
不一定。即使先不用 Fastlane,只做穩定打包 + 上傳 TestFlight,也能先拿到主要收益。

Q6:Runner 放辦公室還是雲端?
看網路與維運能力。要高可用、遠端協作,雲端通常更省心。

Q7:會不會有資安風險?
會,所以要做權限最小化、專用帳號、Secrets 管理與審計記錄。

Q8:如果我要開始,第一步是什麼?
先挑一條最痛的 iOS release pipeline,做 A/B 對照(託管 vs self-hosted)一週,數據會自己說話。

限時優惠