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.
| Fitur | 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:
| 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.