← К техблогу

Почему GitHub Actions iOS CI так медленно на Mac? Настоящее ускорение — self-hosted runner

GitHub Actions iOS CI: Mac mini self-hosted runner и постоянный DerivedData
Хостинг macOS каждый раз с холодного старта; выделенный Mac mini сохраняет DerivedData между job.

Если коротко: iOS CI в GitHub Actions тормозит не потому, что ваш проект «слишком тяжелый», а потому что сборочная цепочка iOS плохо живет в эфемерной среде. Pods, Xcode, подписи, экспорт IPA, загрузка в TestFlight — все это любит постоянный Mac с прогретым кэшем. Поэтому self-hosted runner на Mac — это не «опция для энтузиастов», а реальный способ ускорить и стабилизировать pipeline. По теме также полезно: TCO self-hosted runner, Flutter iOS CI, медленная сборка Xcode и инфраструктурный контекст в войне ИИ-инфраструктуры.

1) Почему iOS CI на GitHub Actions часто медленный

Главная проблема — отсутствие постоянства. Hosted runner поднимается «с нуля», кэши попадают нестабильно, а iOS-цепочка чувствительна к любому промаху кэша. В итоге один и тот же workflow может занимать 14 минут сегодня и 26 завтра.

Суть: hosted хорошо для старта, self-hosted — для предсказуемой скорости в рабочем режиме.

2) На каких шагах теряется время

ЭтапТипичное времяПочему на hosted дольше
pod install4–12 минЧасто заново тянутся зависимости и Specs
xcodebuild archive8–20 минDerivedData не успевает «разогреться»
Signing / Export2–8 минПовторная инициализация keychain и профилей
Upload TestFlight2–10 минПлавающая сеть и очередь загрузки

3) Рабочая архитектура: Linux для тестов, Mac для iOS-релиза

Рекомендуемая архитектура GitHub Actions для iOS CI

Push / PR от разработчика
GitHub Actions (ubuntu-latest)
lint · tests · Android
Self-hosted macOS Runner (Mac mini M4)
pod install → xcodebuild archive → export
TestFlight / App Store Connect

Какие кэши держать на runner

  • CocoaPods cache + Pods
  • DerivedData
  • Ruby/Bundler и build-инструменты

4) Как настроить self-hosted runner, чтобы реально ускориться

  1. Фиксированная машина и пути: иначе кэш не будет стабильно попадать.
  2. Зафиксированные версии: Xcode, CocoaPods, Ruby без «дрейфа».
  3. Ограниченная параллельность: обычно 1–2 iOS job на один Mac.
  4. Тяжелые обновления отдельно: pod repo update в плановом job.
  5. Контроль диска: чистка архивов и DerivedData по расписанию.

Из официальных источников: GitHub про self-hosted runners и Apple Xcode Release Notes.

5) До / после: hosted против self-hosted

СредаПервый билд (cold)Второй билд (warm cache)Стабильность
GitHub hosted macOS16–24 мин14–20 минСредняя
Mac mini M4 self-hosted14–22 мин5–9 минВысокая
Важно: максимальная выгода проявляется на повторных сборках, когда кэши начинают работать как надо.

6) Пример workflow для iOS релиза

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

jobs:
  ios:
    runs-on: [self-hosted, macos, ios]
    steps:
      - uses: actions/checkout@v4
      - name: Install dependencies
        run: |
          bundle install
          cd ios && pod install && cd ..
      - name: 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 iOS CI кейсом.

7) Операционный чеклист: не только быстро, но и стабильно

  • Централизованный signing: сертификаты и профили под контролем.
  • Отдельный пользователь runner: меньше «ручных» конфликтов.
  • Точный retry: перезапускайте только сетевые/загрузочные шаги.
  • Считайте полный TCO: железо, сопровождение и время команды.

Для оценки экономики: разбор TCO и цены.

8) Когда пора переходить на self-hosted

Ситуация командыРекомендацияПочему
≤ 1 iOS релиз в месяцМожно остаться на hostedПереход может не окупиться сразу
2–4 релиза в месяц и постоянное ожидание CIЗапустить pilot self-hostedБыстро видно ROI по времени
Еженедельные релизы / несколько приложенийПереходить на self-hostedМаксимум выигрыша по скорости и предсказуемости
Распределенная командаВыбирать регион ближе к repoНиже задержки при checkout/upload

Большая картина по инфраструктуре: война инфраструктуры вычислений ИИ.

9) FAQ (8 частых вопросов)

Q1: У нас одно iOS-приложение, self-hosted точно нужен?
Не всегда. Но если команда регулярно ждет CI, переход обычно оправдан.

Q2: Что оптимизировать в первую очередь?
CocoaPods и DerivedData. Это самый заметный быстрый эффект.

Q3: Сложно ли поддерживать self-hosted runner?
Старт требует времени, дальше при фиксированных версиях поддержка вполне рутинная.

Q4: Что делать с exit code 65?
Проверить версии Xcode/SDK, затем очистить DerivedData и повторить сборку.

Q5: Нужен ли Fastlane обязательно?
Нет. На первом этапе достаточно стабильной сборки и корректной выгрузки в TestFlight.

Q6: Лучше офисный Mac или облачный?
Для распределенной команды облачный Mac обычно проще и надежнее в эксплуатации.

Q7: Какие риски по безопасности?
Минимальные права, строгая работа с secrets, отдельный runner-user и аудит.

Q8: С чего начать практически?
Сделайте A/B за неделю: hosted vs self-hosted на вашем главном iOS pipeline и сравните фактическое время.

Тарифы