Langsung ke konten

Cara Menyiapkan GitHub Actions Self-Hosted Runner di Mac Mini M4

Panduan langkah demi langkah menyiapkan GitHub Actions self-hosted runner di Mac Mini M4 dedikasi. Build iOS lebih cepat, native Apple Silicon, dan hingga 6,7x lebih murah dibanding runner macOS yang di-host GitHub.

CI/CD Baca 30 menit Diperbarui Oktober 2026

Mengapa Menggunakan Self-Hosted Mac Runner?

Runner macOS yang di-host GitHub memang praktis, tetapi ditagih per menit, sehingga biayanya naik seiring volume build Anda. Dengan $0.062 per menit (tarif runner macOS standar GitHub per Oktober 2026), tim yang menjalankan 75 jam build per bulan membayar sekitar $279. GitHub tidak menagih menit pada self-hosted runner, sehingga Mac mini M4 dedikasi dari My Remote Mac berbiaya tetap $85/bln (USD, belum termasuk pajak) tanpa tagihan per menit, memberi Anda hardware yang sama (atau lebih baik) dengan biaya jauh lebih murah.

Mengapa Menggunakan Self-Hosted Mac Runner?
My Remote Mac Self-Hosted Runner yang Di-host GitHub
Biaya $85/bln (tanpa tagihan per menit) $0.062/menit (~$279/bln untuk 75 jam)
Arsitektur Apple M4, Mac fisik dialokasikan 1:1 VM Apple M1, 3 core, RAM 7 GB (runner standar)
Kecepatan Build ~4 mnt (proyek yang sama) ~12 mnt (proyek menengah)
Cache Persisten Ya (disk persisten) Tidak (ephemeral)
Perangkat Lunak Kustom Akses root penuh Terbatas
Job Bersamaan 1 job per runner; tambah runner atau Mac untuk menskalakan 5 (Free, Pro, Team) / 50 (Enterprise)

Harga My Remote Mac dalam USD, belum termasuk pajak. Angka GitHub: runner macOS standar $0.062 per menit, menurut dokumentasi harga GitHub, per Oktober 2026.

Manfaat Utama: Karena runner bersifat persisten, DerivedData, cache SPM, dan CocoaPods dipertahankan di antara build. Ini saja dapat memangkas waktu build sebesar 50-70% dibanding runner ephemeral yang di-host GitHub yang selalu mulai dari nol setiap kali.

Prasyarat

Sebelum memulai, pastikan Anda memiliki hal-hal berikut:

  • Server build Mac mini M4 khusus dari My Remote Mac (mulai $85/bln, USD, belum termasuk pajak)
  • Akun GitHub dengan akses admin ke repositori atau organisasi Anda
  • Akses SSH ke Mac Mini Anda (disediakan dengan langganan My Remote Mac Anda)
  • Akun Apple Developer (untuk code signing dan provisioning profile)
  • Pemahaman dasar tentang YAML dan perintah terminal

Langkah 1: SSH ke Mac Mini Anda dan Instal Xcode

Pertama, hubungkan ke Mac Mini M4 Anda melalui SSH. Anda telah menerima kredensial saat menyiapkan server My Remote Mac Anda.

Hubungkan melalui 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

Instal 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

Instal Xcode (Versi Lengkap)

Untuk build iOS, Anda memerlukan aplikasi Xcode lengkap. Cara tercepat menginstalnya di server headless adalah menggunakan 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

Instal Simulator 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

Langkah 2: Instal GitHub Actions Runner

Sekarang mari unduh dan konfigurasikan agen GitHub Actions runner. Navigasikan ke repositori Anda di GitHub, buka Settings > Actions > Runners > New self-hosted runner, dan pilih macOS + ARM64.

Unduh dan Konfigurasikan

# 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

Instal sebagai Layanan launchd Persisten

Menjalankan agen secara interaktif tidak masalah untuk pengujian, tetapi untuk produksi Anda memerlukannya agar mulai otomatis dan restart jika crash. GitHub menyediakan skrip instalasi layanan bawaan untuk 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

Layanan sekarang mulai setiap kali pengguna runner login, dan menjalankan ulang runner jika prosesnya mati. LaunchAgent tidak mulai sendiri saat boot: di Mac headless, aktifkan Automatic login untuk pengguna tersebut (System Settings > Users & Groups; tidak tersedia selama FileVault aktif) agar runner kembali online setelah reboot. Anda dapat memverifikasi runner muncul sebagai "Idle" di halaman Settings > Actions > Runners repositori GitHub Anda.

Konfigurasikan Runner untuk Beberapa Repositori (Tingkat Organisasi)

# 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

Langkah 3: Buat Workflow Build iOS Anda

Buat file workflow di repositori Anda pada .github/workflows/ios-build.yml. Workflow ini akan berjalan di self-hosted runner Mac Mini M4 Anda.

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

Tambahkan Code Signing untuk Build Rilis

Untuk deployment TestFlight atau App Store, tambahkan langkah code signing. Simpan sertifikat dan provisioning profile Anda sebagai 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

Langkah 4: Optimalkan Performa

Salah satu keunggulan terbesar dari self-hosted runner adalah caching persisten. Berikut optimasi utama untuk memaksimalkan Mac Mini M4 Anda.

Aktifkan Caching DerivedData

Karena runner bersifat persisten, DerivedData dipertahankan di antara build. Gunakan path DerivedData yang konsisten:

# 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 -

Cache Paket 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

Eksekusi Pengujian Paralel

# 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

Instal xcbeautify untuk Log yang Lebih Baik

# 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

Pemecahan Masalah Umum

Runner muncul sebagai "Offline" di GitHub

Ini biasanya berarti layanan launchd tidak berjalan. Periksa status dan log layanan:

# 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

Code signing gagal dengan "No signing certificate"

Layanan launchd berjalan di bawah konteks pengguna yang berbeda. Pastikan keychain dapat diakses:

# 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

Simulator gagal boot

Simulator terkadang macet. Reset di antara build:

# 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"

Ruang disk menipis

Build Xcode menghasilkan banyak data. Siapkan pembersihan otomatis:

# 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/*

Analisis Biaya

Berikut perbandingan biaya yang detail untuk berbagai ukuran tim dan volume build:

Analisis Biaya
Ukuran Tim Biaya My Remote Mac Build/Bulan Biaya yang Di-host GitHub Penghematan Bulanan
Dev Solo $85/bln 100 build (rata-rata 10 mnt) $62/bln Tidak ada (lebih mahal $23/bln)
Tim Kecil (5) $85/bln 500 build (rata-rata 10 mnt) $310/bln $225/bln
Tim Menengah (15) $229/bln (M4 Pro) 1500 build (rata-rata 10 mnt) $930/bln $701/bln
Enterprise (50+) $458/bln (2x M4 Pro) 5000+ build $3,100+/bln $2,642+/bln

Harga My Remote Mac dalam USD, belum termasuk pajak. Angka GitHub: runner macOS standar $0.062 per menit, menurut dokumentasi harga GitHub, per Oktober 2026.

Kesimpulan: Untuk tim yang menjalankan lebih dari ~140 build per bulan, Mac Mini M4 self-hosted langsung membayar dirinya sendiri. Dan karena build lebih cepat pada hardware persisten dengan cache yang sudah terisi, tim Anda juga menghemat waktu pengembang. Pada 100 build per bulan, seperti di baris Dev Solo, runner yang di-host GitHub $23/bln lebih murah; meski begitu, developer solo tetap bisa memilih Mac dedikasi karena cache yang persisten, build yang lebih cepat, dan tidak ada tagihan per menit. Jika Anda hanya merilis sesekali, paket harian memberi Anda Mac untuk hari rilis mulai $14 per 24 jam (USD, belum termasuk pajak): Mac berhenti setelah 24 jam kecuali Anda mengaktifkan perpanjangan otomatis, dan Anda bisa beralih ke paket bulanan di Mac yang sama.

Siap Mempercepat Build iOS Anda?

Deploy Mac Mini M4 dedikasi sebagai GitHub Actions runner Anda. Mulai dari $85/bulan.

Harga My Remote Mac dalam USD, belum termasuk pajak.

Butuh detail lebih lanjut?

Jelajahi dokumentasi lengkap untuk panduan langkah demi langkah, referensi konfigurasi, dan pemecahan masalah.

Buka dokumentasi →