本文へスキップ

Mac Mini M4 で GitHub Actions セルフホストランナーを構築する方法

専用 Mac Mini M4 で GitHub Actions セルフホストランナーを構築するステップバイステップガイド。高速な iOS ビルド、Apple Silicon ネイティブ、そして GitHub ホスト型 macOS ランナーと比べて最大 6.7 倍のコスト削減。

CI/CD 読了時間 30 分 2026年10月更新

なぜセルフホストの Mac ランナーを使うのか?

GitHub ホスト型の macOS ランナーは便利ですが、分単位の課金のため、ビルド量が増えるほどコストも増えます。1 分あたり$0.062(GitHub 標準 macOS ランナーの料金、2026年10月時点)で、月に 75 時間のビルドを実行するチームは約$279を支払うことになります。GitHub はセルフホストランナーの利用時間に課金しないため、My Remote Mac の専用 Mac mini M4 は分単位の課金なしの定額で月額$85(USD・税抜)。同等(またはそれ以上)のハードウェアをわずかなコストで利用できます。

なぜセルフホストの Mac ランナーを使うのか?
My Remote Mac セルフホスト GitHub ホスト型ランナー
コスト 月額$85(分単位の課金なし) $0.062/分(75 時間で約$279/月)
アーキテクチャ Apple M4、物理Macを1:1で専有 Apple M1 の VM、3 コア、RAM 7 GB(標準ランナー)
ビルド速度 約 4 分(同じプロジェクト) 約 12 分(中規模プロジェクト)
永続的なキャッシュ あり(永続的なディスク) なし(使い捨て)
カスタムソフトウェア 完全な root アクセス 限定的
同時ジョブ数 ランナー 1 つにつき 1 ジョブ。ランナーや Mac を追加して拡張 5(Free・Pro・Team)/ 50(Enterprise)

My Remote Mac の料金は USD・税抜です。 GitHub の数値:標準 macOS ランナー 1 分あたり$0.062(GitHub の料金ドキュメント、2026年10月時点)。

主なメリット: ランナーが永続的であるため、DerivedData、SPM キャッシュ、CocoaPods がビルド間で保持されます。これだけでも、毎回ゼロから始まる使い捨ての GitHub ホスト型ランナーと比べて、ビルド時間を 50〜70% 短縮できます。

前提条件

始める前に、次のものが揃っていることを確認してください。

  • My Remote Mac の専用 Mac mini M4 ビルドサーバー(月額$85から、USD・税抜)
  • リポジトリまたは組織への管理者アクセス権を持つ GitHub アカウント
  • Mac Mini への SSH アクセス(My Remote Mac のサブスクリプションに付属します)
  • Apple Developer アカウント(コード署名とプロビジョニングプロファイル用)
  • YAML と ターミナルコマンドの基本的な知識

ステップ1:Mac Mini に SSH 接続して Xcode をインストールする

まず、Mac Mini M4 に SSH で接続します。My Remote Mac サーバーをセットアップした際に認証情報を受け取っているはずです。

SSH で接続する

# Connect to your Mac Mini M4
ssh admin@your-server-ip

# Verify you're on Apple Silicon
uname -m
# Expected output: arm64

# Check the macOS version
sw_vers

Xcode Command Line Tools をインストールする

# Install Command Line Tools
xcode-select --install

# Accept the license agreement
sudo xcodebuild -license accept

# Verify installation
xcode-select -p
# /Library/Developer/CommandLineTools

Xcode(フルバージョン)をインストールする

iOS ビルドには Xcode アプリケーションの完全版が必要です。ヘッドレスサーバーにインストールする最も速い方法は xcodes を使うことです。

# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Add Homebrew to PATH
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

# Install xcodes CLI tool
brew install xcodes

# List available Xcode versions
xcodes list

# Install the latest release of Xcode (asks for your Apple Account)
xcodes install --latest

# Show the installed versions and their paths
xcodes installed
# e.g. 27.0 (27A266a)  /Applications/Xcode-27.0.0.app

# Set it as the active Xcode (interactive SSH session: sudo asks for your password)
sudo xcode-select -s /Applications/Xcode-27.0.0.app/Contents/Developer

# Verify
xcodebuild -version
# Xcode 27.0
# Build version 27A266a

iOS シミュレーターをインストールする

# Install the iOS simulator runtime that matches your Xcode
xcodebuild -downloadPlatform iOS

# Verify simulator availability
xcrun simctl list runtimes
xcrun simctl list devices available

ステップ2:GitHub Actions ランナーをインストールする

次に、GitHub Actions ランナーエージェントをダウンロードして設定します。GitHub 上でリポジトリに移動し、Settings > Actions > Runners > New self-hosted runner に進んで、macOS + ARM64 を選択します。

ダウンロードして設定する

# Create a directory for the runner
mkdir -p ~/actions-runner && cd ~/actions-runner

# Download the latest runner package (macOS ARM64). The same commands, with the
# current version and its checksum, are shown on Settings > Actions > Runners >
# New self-hosted runner
RUNNER_VERSION=$(curl -fsSL https://api.github.com/repos/actions/runner/releases/latest \
  | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
curl -o "actions-runner-osx-arm64-${RUNNER_VERSION}.tar.gz" -L \
  "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-osx-arm64-${RUNNER_VERSION}.tar.gz"

# Extract the package
tar xzf "actions-runner-osx-arm64-${RUNNER_VERSION}.tar.gz"

# Configure the runner
# Replace YOUR_TOKEN with the token from GitHub Settings
./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO \
  --token YOUR_TOKEN \
  --name "mac-mini-m4-runner" \
  --labels "self-hosted,macOS,ARM64,M4" \
  --work "_work"

# Test the runner interactively first
./run.sh

永続的な launchd サービスとしてインストールする

エージェントを対話的に実行するのはテストには問題ありませんが、本番では自動的に開始し、クラッシュしても再起動する必要があります。GitHub は macOS 向けの組み込みサービスインストールスクリプトを提供しています。

# Install as a launchd service (a LaunchAgent; no sudo on macOS)
cd ~/actions-runner
./svc.sh install

# Start the service
./svc.sh start

# Check the service status
./svc.sh status

# View the launchd plist (for reference)
cat ~/Library/LaunchAgents/actions.runner.*.plist

これでサービスはランナー用ユーザーのログイン時に毎回起動し、プロセスが停止すればランナーを再起動します。LaunchAgent は Mac の起動時に単独では開始しません。ヘッドレスの Mac では、そのユーザーの自動ログインを有効にしてください(システム設定 > ユーザとグループ。FileVault がオンの間は利用できません)。これで再起動後もランナーがオンラインに戻ります。ランナーがリポジトリの Settings > Actions > Runners ページに「Idle」として表示されることを確認できます。

複数のリポジトリ向けにランナーを設定する(組織レベル)

# For an organization-level runner, use the organization URL:
./config.sh --url https://github.com/YOUR_ORG \
  --token YOUR_ORG_TOKEN \
  --name "mac-mini-m4-org-runner" \
  --labels "self-hosted,macOS,ARM64,M4" \
  --runnergroup "Default" \
  --work "_work"

# This allows ALL repositories in your organization to use this runner

ステップ3:iOS ビルドワークフローを作成する

リポジトリ内の .github/workflows/ios-build.yml にワークフローファイルを作成します。このワークフローは、セルフホストの Mac Mini M4 ランナー上で実行されます。

name: iOS Build & Test

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: [self-hosted, macOS, ARM64, M4]

    # bash -eo pipefail: a failing xcodebuild fails the step even when piped to xcbeautify
    defaults:
      run:
        shell: bash

    env:
      SCHEME: "MyApp"
      # Any device listed by: xcrun simctl list devices available
      DESTINATION: "platform=iOS Simulator,name=iPhone 17,OS=latest"
      # Pick the Xcode for this job without sudo (path from: xcodes installed)
      DEVELOPER_DIR: /Applications/Xcode-27.0.0.app/Contents/Developer
      # Caches sit next to the checkout: actions/checkout cleans the checkout
      # (git clean -ffdx) on every run, but not its parent folder
      DERIVED_DATA: ${{ github.workspace }}/../DerivedData
      SPM_CACHE: ${{ github.workspace }}/../SourcePackages

    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Show the selected Xcode
        run: xcodebuild -version

      - name: Resolve Swift Package Dependencies
        run: |
          xcodebuild -resolvePackageDependencies \
            -scheme "$SCHEME" \
            -clonedSourcePackagesDirPath "$SPM_CACHE"

      - name: Install xcbeautify
        run: command -v xcbeautify >/dev/null || brew install xcbeautify

      - name: Build the app
        run: |
          xcodebuild build \
            -scheme "$SCHEME" \
            -destination "$DESTINATION" \
            -clonedSourcePackagesDirPath "$SPM_CACHE" \
            -derivedDataPath "$DERIVED_DATA" \
            | xcbeautify

      - name: Run unit tests
        run: |
          xcodebuild test \
            -scheme "$SCHEME" \
            -destination "$DESTINATION" \
            -clonedSourcePackagesDirPath "$SPM_CACHE" \
            -derivedDataPath "$DERIVED_DATA" \
            -resultBundlePath TestResults.xcresult \
            | xcbeautify

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v7
        with:
          name: test-results
          path: TestResults.xcresult

リリースビルドのためのコード署名を追加する

TestFlight や App Store へのデプロイには、コード署名のステップを追加します。証明書とプロビジョニングプロファイルは GitHub Secrets として保存してください。

  deploy:
    needs: build
    runs-on: [self-hosted, macOS, ARM64, M4]
    if: github.ref == 'refs/heads/main'

    env:
      # Same Xcode as the build job (App Store Connect requires Xcode 26 or later)
      DEVELOPER_DIR: /Applications/Xcode-27.0.0.app/Contents/Developer

    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Install certificate and provisioning profile
        env:
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          # Create a temporary keychain
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
          security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

          # Import certificate
          CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
          echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
          security import $CERTIFICATE_PATH -P "$P12_PASSWORD" \
            -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH

          # Install provisioning profile (the folder Xcode 16 and later read)
          PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
          echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
          mkdir -p ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles
          cp $PP_PATH ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles

      - name: Build for distribution
        run: |
          xcodebuild archive \
            -scheme "MyApp" \
            -archivePath DerivedData/MyApp.xcarchive \
            -destination "generic/platform=iOS" \
            CODE_SIGN_STYLE=Manual

      - name: Export IPA
        run: |
          xcodebuild -exportArchive \
            -archivePath DerivedData/MyApp.xcarchive \
            -exportOptionsPlist ExportOptions.plist \
            -exportPath DerivedData/Export

      - name: Upload to TestFlight
        env:
          APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
          APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
        run: |
          # altool reads the key from ~/.appstoreconnect/private_keys/AuthKey_<KEY_ID>.p8
          xcrun altool --upload-app \
            -f DerivedData/Export/MyApp.ipa \
            -t ios \
            --apiKey $APP_STORE_CONNECT_API_KEY \
            --apiIssuer $APP_STORE_CONNECT_ISSUER_ID

      # The runner is persistent: remove the signing files at the end of every job
      - name: Clean up keychain and provisioning profile
        if: ${{ always() }}
        run: |
          security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true
          rm -f ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles/build_pp.mobileprovision

ステップ4:パフォーマンスを最適化する

セルフホストランナーの最大の利点の 1 つは、永続的なキャッシュです。ここでは、Mac Mini M4 を最大限に活用するための主要な最適化を紹介します。

DerivedData のキャッシュを有効にする

ランナーが永続的であるため、DerivedData はビルド間で保持されます。一貫した DerivedData のパスを使用してください。

# In your workflow, keep DerivedData next to the checkout (job env, as above):
#   DERIVED_DATA: ${{ github.workspace }}/../DerivedData
#   ... -derivedDataPath "$DERIVED_DATA"
# Each repository then gets ~/actions-runner/_work/<repo>/DerivedData

# On the runner, remove the DerivedData of repositories not built for 7 days
# (cron job every Sunday at 03:00, keeps your existing crontab entries)
(crontab -l 2>/dev/null; echo '0 3 * * 0 find "$HOME"/actions-runner/_work/*/DerivedData -maxdepth 0 -type d -mtime +7 -exec rm -rf {} +') \
  | crontab -

SPM パッケージをキャッシュする

# Use clonedSourcePackagesDirPath to keep SPM packages on disk
# (SPM_CACHE points outside the checkout, as in the workflow above)
xcodebuild build \
  -scheme "MyApp" \
  -clonedSourcePackagesDirPath "$SPM_CACHE" \
  -derivedDataPath "$DERIVED_DATA"

# This avoids re-downloading packages on every build

テストの並列実行

# Run tests in parallel across multiple simulators
# (devices from: xcrun simctl list devices available)
set -o pipefail
xcodebuild test \
  -scheme "MyApp" \
  -destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
  -destination "platform=iOS Simulator,name=iPhone 17 Pro,OS=latest" \
  -parallel-testing-enabled YES \
  -maximum-parallel-testing-workers 4 \
  -derivedDataPath "$DERIVED_DATA" \
  | xcbeautify

より見やすいログのために xcbeautify をインストールする

# xcbeautify formats Xcode output for CI environments
brew install xcbeautify

# Use it by piping xcodebuild output (pipefail keeps xcodebuild's exit status):
set -o pipefail && xcodebuild build -scheme "MyApp" | xcbeautify

よくある問題のトラブルシューティング

GitHub でランナーが「Offline」と表示される

これは通常、launchd サービスが実行されていないことを意味します。サービスの状態とログを確認してください。

# Check service status
./svc.sh status

# View logs
cat ~/actions-runner/_diag/Runner_*.log | tail -50

# Restart the service
./svc.sh stop
./svc.sh start

コード署名が「No signing certificate」で失敗する

launchd サービスは異なるユーザーコンテキストで実行されます。キーチェーンにアクセスできることを確認してください。

# Ensure the login keychain is unlocked for the runner user
security unlock-keychain -p "YOUR_PASSWORD" ~/Library/Keychains/login.keychain-db

# Or use a dedicated keychain in your workflow (recommended)
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain

シミュレーターの起動に失敗する

シミュレーターは時々動かなくなることがあります。ビルド間でリセットしてください。

# Shutdown all running simulators
xcrun simctl shutdown all

# Erase all simulator data (nuclear option)
xcrun simctl erase all

# Boot a specific simulator
xcrun simctl boot "iPhone 17"

ディスク容量が不足してくる

Xcode のビルドは大量のデータを生成します。自動クリーンアップを設定してください。

# Clean old DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData/*

# Remove simulators whose runtime is no longer installed
xcrun simctl delete unavailable

# Clean Homebrew cache
brew cleanup --prune=7

# Remove old Xcode archives
rm -rf ~/Library/Developer/Xcode/Archives/*

コスト分析

さまざまなチーム規模とビルド量に対する詳細なコスト比較を示します。

コスト分析
チーム規模 My Remote Mac の費用 月間ビルド数 GitHub ホスト型の費用 月間節約額
個人開発者 月額$85 100 ビルド(平均 10 分) 月額$62 なし(月額$23割高)
小規模チーム(5 人) 月額$85 500 ビルド(平均 10 分) 月額$310 月額$225
中規模チーム(15 人) 月額$229(M4 Pro) 1500 ビルド(平均 10 分) 月額$930 月額$701
エンタープライズ(50 人以上) 月額$458(M4 Pro 2 台) 5000 ビルド以上 月額$3,100以上 月額$2,642以上

My Remote Mac の料金は USD・税抜です。 GitHub の数値:標準 macOS ランナー 1 分あたり$0.062(GitHub の料金ドキュメント、2026年10月時点)。

結論: 月に約 140 ビルドを超えるチームであれば、セルフホストの Mac Mini M4 はすぐに元が取れます。さらに、ウォームキャッシュを備えた永続的なハードウェアではビルドが速くなるため、チームの開発時間も節約できます。個人開発者の行のように月 100 ビルドの場合は、GitHub ホスト型ランナーのほうが月額$23安くなります。それでも個人開発者が専用 Mac を選ぶ理由はあります。キャッシュが永続的に保持され、ビルドが速く、分単位の課金もありません。リリースがたまにしかない場合は、日額プランでリリース日に Mac を 24 時間あたり$14から(USD・税抜)利用できます。自動更新をオンにしない限り 24 時間で停止し、同じ Mac のまま月額プランに切り替えることもできます。

iOS ビルドを高速化する準備はできましたか?

専用 Mac Mini M4 を GitHub Actions ランナーとしてデプロイしましょう。月額$85から。

My Remote Mac の料金は USD・税抜です。

さらに詳しい情報をお探しですか?

ステップバイステップのセットアップ手順、設定リファレンス、トラブルシューティングについては、完全なドキュメントをご覧ください。

ドキュメントを開く →