Mengapa Menggunakan Self-Hosted Runner CircleCI di Mac?
CircleCI menawarkan lingkungan eksekusi macOS berbasis cloud, tetapi disertai biaya dan keterbatasan yang signifikan. CircleCI menagih job macOS dalam kredit: resource class m4pro.medium memakai 200 kredit per menit (sekitar $0.12) dan m4pro.large 400 kredit (sekitar $0.24), per Oktober 2026. Untuk tim yang sering menjalankan build iOS, ini dapat dengan cepat mencapai $300-$500 per bulan atau lebih.
Self-hosted runner di Mac Mini M4 dedikasi dari MyRemoteMac menghilangkan penagihan per menit sepenuhnya. Anda mendapatkan mesin Apple Silicon dedikasi dengan penyimpanan persisten, cache yang sudah terisi, dan akses root penuh dengan biaya bulanan tetap mulai dari $85/bulan. Berikut perbandingan detailnya:
| Fitur | MyRemoteMac Self-Hosted | CircleCI Cloud macOS |
|---|---|---|
| Model Biaya | $85/bln tetap (menit tanpa batas) | 200 kredit/mnt pada m4pro.medium (~$0.12/menit) |
| Arsitektur | Mac Apple M4 fisik, dialokasikan 1:1 | Mesin virtual M4 Pro (6 vCPU, 28 GB) |
| Kecepatan Build | ~5 mnt (proyek yang sama) | ~14 mnt (proyek iOS menengah) |
| Persistensi Cache | Persisten di disk (instan) | Ephemeral (harus dipulihkan tiap job) |
| Waktu Tunggu Antrean | 0 dtk (hardware dedikasi) | 30 dtk - 5 mnt (bervariasi per paket) |
| Privasi / Kontrol Data | Mesin dedikasi, kontrol penuh | Infrastruktur bersama |
| Perangkat Lunak Kustom | Akses root penuh, perangkat lunak apa pun | Hanya image prainstal |
Harga My Remote Mac dalam USD, belum termasuk pajak.
Manfaat Utama: Dengan self-hosted runner yang persisten, cache DerivedData, Swift Package Manager, dan CocoaPods dipertahankan di antara build. Ini saja dapat memangkas waktu build sebesar 50-70% dibanding lingkungan macOS cloud ephemeral CircleCI yang selalu mulai dari nol setiap kali. Dikombinasikan dengan performa single-thread chip M4 yang unggul, build iOS Anda akan jauh lebih cepat.
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)
- Organisasi CircleCI tempat Anda menjadi admin (self-hosted runner tersedia di semua paket, termasuk Free yang mengizinkan 5 tugas self-hosted bersamaan per Oktober 2026)
- Akses SSH ke Mac Mini Anda (disediakan dengan langganan MyRemoteMac Anda)
- Akun Apple Developer (untuk code signing dan provisioning profile)
- Pemahaman dasar tentang YAML, konfigurasi CircleCI, dan perintah terminal
Langkah 1: SSH ke Mac Mini Anda dan Instal Dependensi
Pertama, hubungkan ke Mac Mini M4 Anda melalui SSH. Anda telah menerima kredensial saat menyiapkan server MyRemoteMac Anda. Kita perlu menginstal Xcode dan alat yang diperlukan untuk build iOS sebelum menyiapkan runner CircleCI.
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
# Show the macOS version
sw_vers
Instal Homebrew dan Xcode Command Line Tools
# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Add Homebrew to PATH: zsh for your sessions, bash for CircleCI job steps
# (CircleCI runs macOS steps in a bash login shell)
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.bash_profile
eval "$(/opt/homebrew/bin/brew shellenv)"
# Install Xcode Command Line Tools
xcode-select --install
# Accept the license agreement
sudo xcodebuild -license accept
Instal Xcode (Versi Lengkap)
Untuk build iOS, Anda memerlukan aplikasi Xcode lengkap. Sejak 28 April 2026, App Store Connect hanya menerima build yang dibuat dengan Xcode 26 atau lebih baru, jadi instal rilis terbaru dengan xcodes, cara tercepat di server headless:
# Install the xcodes CLI tool
brew install xcodes
# Install the latest release of Xcode (asks for your Apple Account)
xcodes install --latest
# Show where it was installed, then make it the default Xcode
xcodes installed
# e.g. 27.0 (27A266a) /Applications/Xcode-27.0.0.app
sudo xcode-select -s /Applications/Xcode-27.0.0.app/Contents/Developer
# Verify installation
xcodebuild -version
Instal Simulator iOS dan Build Tools
# Install the current iOS simulator runtime
xcodebuild -downloadPlatform iOS
# Verify simulator availability
xcrun simctl list runtimes
# Install additional build tools
brew install xcbeautify fastlane swiftlint
Langkah 2: Instal Agen Runner CircleCI
Machine runner 3 dari CircleCI menghubungkan Mac mini Anda ke CircleCI: ia menerima job dan menjalankannya langsung di host macOS Anda. Di macOS, CircleCI merekomendasikan instalasi melalui paket Homebrew-nya. Ini berbeda dari container runner (yang hanya bekerja di Linux).
Pilih Pengguna yang Menjalankan Job Anda
Machine runner 3 menjalankan setiap job sebagai pengguna macOS yang memulainya, dengan Homebrew, keychain, dan Xcode milik pengguna itu. Instal dan jalankan dengan pengguna admin Anda, pemilik Homebrew, bukan sebagai root: Homebrew menolak berjalan sebagai root.
# Run the steps below as the user that will run CI jobs, not with sudo
whoami
# Homebrew must work for this user (prints /opt/homebrew)
brew --prefix
Instal Machine Runner 3 dengan Homebrew
# Add CircleCI's Homebrew tap and install machine runner 3
brew tap circleci-public/circleci
brew install circleci-runner
# Check that macOS accepts the binary notarized by CircleCI
spctl -a -vvv -t install "$(brew --prefix)/bin/circleci-runner"
# Expected: accepted, source=Notarized Developer ID,
# origin=Developer ID Application: Circle Internet Services Inc.
# Remove the quarantine attribute (asks for your macOS password)
sudo xattr -r -d com.apple.quarantine "$(brew --prefix)/bin/circleci-runner"
Konfigurasikan Agen Runner
Paket Homebrew membuat file konfigurasi. Anda memerlukan token autentikasi runner dari aplikasi web CircleCI (dibahas di Langkah 3). Untuk saat ini, atur nama runner dan biarkan placeholder untuk tokennya:
# Open the configuration file created by the Homebrew package
nano "$HOME/Library/Preferences/com.circleci.runner/config.yaml"
# Contents (the token comes in Step 3):
runner:
name: "mac-mini-m4-runner"
working_directory: "/Users/$USER/Library/com.circleci.runner/workdir"
cleanup_working_directory: true
api:
auth_token: "YOUR_RUNNER_TOKEN_HERE"
# The file will hold a secret: make it readable by you only
chmod 600 "$HOME/Library/Preferences/com.circleci.runner/config.yaml"
Siapkan Layanan launchd untuk Mac Headless
Paket Homebrew juga menginstal agen launchd, com.circleci.runner. Agen biasanya mulai saat pengguna Anda login; di Mac yang hanya Anda akses lewat SSH, penyiapan headless dari CircleCI memindahkannya ke /Library/LaunchAgents agar dimuat setelah reboot. Jalankan di Langkah 3, setelah token terpasang:
# The launchd agent installed by the Homebrew package
ls ~/Library/LaunchAgents/com.circleci.runner.plist
# Headless Mac reached over SSH only: move it to the per-user agents
# folder so it loads after a reboot (CircleCI's headless setup)
sudo mv ~/Library/LaunchAgents/com.circleci.runner.plist /Library/LaunchAgents/
Penting: Runner tidak akan terhubung sampai config.yaml berisi token autentikasi yang valid. Buat token di Langkah 3, tambahkan ke file, lalu jalankan layanannya.
Langkah 3: Konfigurasikan Runner di Dasbor CircleCI
Sekarang Anda perlu mendaftarkan runner Anda di antarmuka web CircleCI dan menghasilkan token autentikasi. Ini menghubungkan Mac Mini Anda ke organisasi CircleCI Anda.
Buat Resource Class
Di CircleCI, self-hosted runner diorganisasikan berdasarkan resource class. Resource class adalah label yang memetakan .circleci/config.yml Anda ke sekumpulan runner tertentu.
- Di aplikasi web CircleCI, buka Runners di sidebar (Anda harus admin organisasi)
- Klik "Create Resource Class"
- Atur Namespace ke nama organisasi Anda (mis.,
your-org) - Atur nama Resource Class ke sesuatu yang deskriptif (mis.,
mac-runner) - Ini membuat identifier resource class:
your-org/mac-runner
Hasilkan Token Autentikasi Runner
- Klik "Save and continue": CircleCI membuat resource class dan menampilkan token autentikasinya
- Salin token segera dan simpan dengan aman: token hanya ditampilkan sekali
- Hilang? Buat yang baru dengan CircleCI CLI:
circleci runner token create your-org/mac-runner mac-mini-m4
Tambahkan Token dan Jalankan Runner
# SSH back into your Mac mini
ssh admin@your-server-ip
# Replace YOUR_RUNNER_TOKEN_HERE with the resource class token
nano "$HOME/Library/Preferences/com.circleci.runner/config.yaml"
# Start the service. Headless Mac (plist moved to /Library/LaunchAgents):
launchctl bootstrap user/$(id -u) /Library/LaunchAgents/com.circleci.runner.plist
launchctl enable user/$(id -u)/com.circleci.runner
launchctl kickstart -k user/$(id -u)/com.circleci.runner
# Or, from a desktop session (VNC), with the plist left in ~/Library/LaunchAgents:
# launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.circleci.runner.plist
# launchctl enable gui/$(id -u)/com.circleci.runner
# launchctl kickstart -k gui/$(id -u)/com.circleci.runner
# Verify the service, then read the runner log
launchctl print user/$(id -u)/com.circleci.runner
tail -20 "$HOME/Library/Logs/com.circleci.runner/runner.log"
Setelah terhubung, runner tercantum di bagian Runners pada aplikasi web CircleCI, dan circleci runner instance list your-org/mac-runner juga menampilkannya. Jika tidak muncul dalam 60 detik, periksa file log untuk pesan kesalahan.
Menggunakan CircleCI CLI (Alternatif)
Anda juga dapat mengelola runner melalui CircleCI CLI:
# Install the CircleCI CLI
brew install circleci
# Authenticate with CircleCI
circleci setup
# Create the namespace (once per organization)
circleci namespace create your-org --org-id YOUR_ORGANIZATION_ID
# Create the resource class and print its token
circleci runner resource-class create your-org/mac-runner \
"Mac mini M4 runner" --generate-token
# List the runners connected to this resource class
circleci runner instance list your-org/mac-runner
Langkah 4: Buat .circleci/config.yml Anda untuk Build iOS
Buat file .circleci/config.yml di root repositori Anda. Perbedaan utama dari konfigurasi CircleCI standar adalah penggunaan machine: true dengan resource_class kustom Anda untuk menargetkan self-hosted runner Anda.
version: 2.1
jobs:
build-and-test:
machine: true
resource_class: your-org/mac-runner
environment:
# Xcode for this job, without sudo (path from `xcodes installed`)
DEVELOPER_DIR: "/Applications/Xcode-27.0.0.app/Contents/Developer"
SCHEME: "MyApp"
# Any device listed by: xcrun simctl list devices available
DESTINATION: "platform=iOS Simulator,name=iPhone 17,OS=latest"
steps:
- checkout
- run:
name: Show the Xcode version
command: xcodebuild -version
# Caches live in $HOME, outside the job's working directory,
# so they survive cleanup_working_directory between jobs
- run:
name: Resolve Swift Package Dependencies
command: |
xcodebuild -resolvePackageDependencies \
-scheme "$SCHEME" \
-clonedSourcePackagesDirPath "$HOME/spm-cache"
- run:
name: Build the app
command: |
xcodebuild build \
-scheme "$SCHEME" \
-destination "generic/platform=iOS Simulator" \
-clonedSourcePackagesDirPath "$HOME/spm-cache" \
-derivedDataPath "$HOME/DerivedData/$SCHEME" \
| xcbeautify
# store_test_results needs JUnit XML: xcbeautify writes test-results/junit.xml
- run:
name: Run unit tests
command: |
xcodebuild test \
-scheme "$SCHEME" \
-destination "$DESTINATION" \
-clonedSourcePackagesDirPath "$HOME/spm-cache" \
-derivedDataPath "$HOME/DerivedData/$SCHEME" \
-resultBundlePath TestResults.xcresult \
| xcbeautify --report junit --report-path test-results
- store_test_results:
path: test-results
- store_artifacts:
path: TestResults.xcresult
destination: test-results
workflows:
ios-pipeline:
jobs:
- build-and-test:
filters:
branches:
only:
- main
- develop
Catatan: Nilai resource_class: your-org/mac-runner harus sama persis dengan resource class yang Anda buat di dasbor CircleCI. Jika tidak cocok, job akan tetap dalam antrean tanpa batas waktu.
Langkah 5: Optimalkan dengan Caching dan Paralelisme
Salah satu keunggulan terbesar dari self-hosted runner adalah caching persisten. Karena filesystem runner bersifat persisten, Anda tidak perlu mengunggah dan mengunduh cache seperti pada sumber daya cloud CircleCI. Namun, ada optimasi tambahan untuk memaksimalkan kecepatan build Anda.
Manfaatkan DerivedData Persisten
Karena disk runner bertahan di antara build, DerivedData sudah otomatis di-cache. Gunakan path yang konsisten:
# In your config.yml steps, always use a consistent DerivedData path:
- run:
name: Build with persistent cache
command: |
xcodebuild build \
-scheme "$SCHEME" \
-destination "$DESTINATION" \
-derivedDataPath ~/DerivedData/"$SCHEME" \
-clonedSourcePackagesDirPath ~/spm-cache \
| xcbeautify
# Periodically clean old DerivedData on the runner (cron job):
# 0 3 * * 0 find ~/DerivedData -mindepth 1 -maxdepth 1 -mtime +7 -exec rm -rf {} +
Persistensi Workspace di Antara Job
Gunakan workspace CircleCI untuk meneruskan produk hasil build ke job pengujian, yang bisa berjalan di Mac lain dalam resource class yang sama:
jobs:
build:
machine: true
resource_class: your-org/mac-runner
steps:
- checkout
- run:
name: Build for testing
command: |
xcodebuild build-for-testing \
-scheme "MyApp" \
-destination "generic/platform=iOS Simulator" \
-derivedDataPath DerivedData \
| xcbeautify
# Pass only the built products, not the whole DerivedData
- persist_to_workspace:
root: .
paths:
- DerivedData/Build/Products
test:
machine: true
resource_class: your-org/mac-runner
steps:
- attach_workspace:
at: .
- run:
name: Run the tests built by the previous job
command: |
xcodebuild test-without-building \
-xctestrun "$(ls DerivedData/Build/Products/*.xctestrun | head -n 1)" \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
| xcbeautify --report junit --report-path test-results
- store_test_results:
path: test-results
workflows:
build-test:
jobs:
- build
- test:
requires:
- build
Eksekusi Pengujian Paralel
- run:
name: Run tests in parallel
command: |
# Xcode clones the simulator and runs test classes on up to 4 clones
xcodebuild test \
-scheme "MyApp" \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
-parallel-testing-enabled YES \
-maximum-parallel-testing-workers 4 \
-derivedDataPath ~/DerivedData/MyApp \
| xcbeautify
Test Splitting CircleCI
Untuk suite pengujian besar, test splitting CircleCI membagi kelas pengujian ke beberapa tugas paralel. Setiap tugas memerlukan runner sendiri, jadi parallelism: 3 memerlukan tiga Mac dalam resource class; di satu Mac, tugas-tugas berjalan bergantian:
jobs:
test:
machine: true
resource_class: your-org/mac-runner
# One task per runner: 3 tasks need 3 Macs in this resource class
parallelism: 3
steps:
- checkout
- run:
name: Split test classes and run this task's share
command: |
# One XCTest class per file, named like the file (LoginTests.swift)
CLASSES=$(circleci tests glob "MyAppTests/**/*Tests.swift" \
| xargs -n1 basename | sed 's/\.swift$//' \
| circleci tests split)
# -only-testing takes Target/Class identifiers, not file paths
ONLY_TESTING=""
for CLASS in $CLASSES; do
ONLY_TESTING="$ONLY_TESTING -only-testing:MyAppTests/$CLASS"
done
xcodebuild test \
-scheme "MyApp" \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
$ONLY_TESTING \
| xcbeautify --report junit --report-path test-results
- store_test_results:
path: test-results
Contoh Workflow
Berikut contoh workflow lengkap yang siap produksi untuk skenario CI/CD iOS yang umum.
Pipeline Build, Test, dan Deploy iOS Lengkap
version: 2.1
jobs:
lint:
machine: true
resource_class: your-org/mac-runner
steps:
- checkout
- run:
name: Run SwiftLint
command: swiftlint lint --reporter json > swiftlint-results.json || true
- store_artifacts:
path: swiftlint-results.json
build-and-test:
machine: true
resource_class: your-org/mac-runner
environment:
# Xcode for this job, without sudo (path from `xcodes installed`)
DEVELOPER_DIR: "/Applications/Xcode-27.0.0.app/Contents/Developer"
steps:
- checkout
- run:
name: Resolve dependencies
command: |
xcodebuild -resolvePackageDependencies \
-scheme "MyApp" \
-clonedSourcePackagesDirPath ~/spm-cache
- run:
name: Build and test
command: |
xcodebuild test \
-scheme "MyApp" \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
-clonedSourcePackagesDirPath ~/spm-cache \
-derivedDataPath ~/DerivedData/MyApp \
-resultBundlePath TestResults.xcresult \
-parallel-testing-enabled YES \
| xcbeautify --report junit --report-path test-results
- store_test_results:
path: test-results
- store_artifacts:
path: TestResults.xcresult
deploy-testflight:
machine: true
resource_class: your-org/mac-runner
environment:
DEVELOPER_DIR: "/Applications/Xcode-27.0.0.app/Contents/Developer"
steps:
- checkout
- run:
name: Install dependencies
command: bundle install
# MATCH_PASSWORD and the App Store Connect API key variables
# (ASC_KEY_ID, ASC_ISSUER_ID, ASC_KEY_CONTENT) come from a CircleCI
# context or the project's environment variables
- run:
name: Build and upload to TestFlight
command: bundle exec fastlane beta
workflows:
ios-pipeline:
jobs:
- lint
- build-and-test:
requires:
- lint
- deploy-testflight:
requires:
- build-and-test
filters:
branches:
only: main
Contoh Integrasi Fastlane
Jika Anda menggunakan Fastlane untuk otomatisasi build, berikut cara mengintegrasikannya dengan self-hosted runner CircleCI Anda:
# Fastfile (fastlane/Fastfile)
default_platform(:ios)
platform :ios do
desc "Run tests"
lane :test do
scan(
scheme: "MyApp",
device: "iPhone 17",
derived_data_path: File.expand_path("~/DerivedData/MyApp"),
result_bundle: true,
output_directory: "./test_output",
# store_test_results reads JUnit XML
output_types: "junit",
output_files: "junit.xml"
)
end
desc "Build and push to TestFlight"
lane :beta do
# App Store Connect API key: no Apple ID password or 2FA on the runner
app_store_connect_api_key(
key_id: ENV["ASC_KEY_ID"],
issuer_id: ENV["ASC_ISSUER_ID"],
key_content: ENV["ASC_KEY_CONTENT"]
)
match(type: "appstore", readonly: true)
increment_build_number(
build_number: ENV["CIRCLE_BUILD_NUM"]
)
gym(
scheme: "MyApp",
export_method: "app-store",
derived_data_path: File.expand_path("~/DerivedData/MyApp")
)
pilot(skip_waiting_for_build_processing: true)
end
end
# .circleci/config.yml using Fastlane
version: 2.1
jobs:
fastlane-test:
machine: true
resource_class: your-org/mac-runner
steps:
- checkout
- run:
name: Install dependencies
command: bundle install
- run:
name: Run Fastlane tests
command: bundle exec fastlane test
- store_test_results:
path: ./test_output
- store_artifacts:
path: ./test_output
fastlane-deploy:
machine: true
resource_class: your-org/mac-runner
steps:
- checkout
- run:
name: Install dependencies
command: bundle install
- run:
name: Deploy to TestFlight
command: bundle exec fastlane beta
workflows:
ios-workflow:
jobs:
- fastlane-test
- fastlane-deploy:
requires:
- fastlane-test
filters:
branches:
only: main
Perbandingan Performa
Berikut perbandingan performa dan biaya yang detail antara sumber daya macOS cloud CircleCI dan Mac Mini M4 self-hosted dari MyRemoteMac:
Benchmark Waktu Build
| Skenario | Mac Mini M4 Self-Hosted | CircleCI Cloud macOS | Peningkatan |
|---|---|---|---|
| Clean build (aplikasi menengah) | 5 mnt | 14 mnt | 2.8x lebih cepat |
| Build inkremental | 1.5 mnt | 14 mnt (tanpa cache) | 9.3x lebih cepat |
| Resolusi dependensi SPM | 5 dtk (di-cache di disk) | 3-5 mnt (unduh setiap kali) | ~60x lebih cepat |
| Suite unit test (500 pengujian) | 2.5 mnt | 8 mnt | 3.2x lebih cepat |
| Waktu tunggu antrean | 0 dtk | 30 dtk - 5 mnt | Instan |
Analisis Biaya Bulanan
| Ukuran Tim | Biaya MyRemoteMac | Build/Bulan | Biaya CircleCI Cloud | Penghematan Bulanan |
|---|---|---|---|---|
| Dev Solo | $85/bln | 100 build (rata-rata 10 mnt) | $120/bln | $35/bln |
| Tim Kecil (5) | $85/bln | 500 build (rata-rata 10 mnt) | $600/bln | $515/bln |
| Tim Menengah (15) | $229/bln (M4 Pro) | 1500 build (rata-rata 10 mnt) | $1,800/bln | $1,571/bln |
| Enterprise (50+) | $458/bln (2x M4 Pro) | 5000+ build | $6,000+/bln | $5,542+/bln |
Harga My Remote Mac dalam USD, belum termasuk pajak.
Kesimpulan: Dengan tarif CircleCI untuk kelas m4pro.medium (200 kredit, sekitar $0.12 per menit, per Oktober 2026), satu build 10 menit berbiaya sekitar $1.20, sehingga Mac mini M4 khusus seharga $85/bln (USD, belum termasuk pajak) lebih murah mulai sekitar 71 build per bulan. Cache yang sudah terisi di Mac persisten juga memangkas waktu tunggu tim Anda di setiap build.
Pemecahan Masalah Umum
Runner terputus atau muncul sebagai "Offline"
Runner bisa kehilangan koneksi setelah masalah jaringan, crash, atau reboot. Jika dijalankan di domain gui, runner baru kembali saat penggunanya login; penyiapan headless dari Langkah 2 menghindari hal itu. Periksa layanan dan log-nya:
# Check the service (use gui/ instead of user/ if you started it
# from a desktop session)
launchctl print user/$(id -u)/com.circleci.runner
# View the runner log
tail -50 "$HOME/Library/Logs/com.circleci.runner/runner.log"
# Restart the service
launchctl kickstart -k user/$(id -u)/com.circleci.runner
# If the token was revoked, create a new one, put it in
# ~/Library/Preferences/com.circleci.runner/config.yaml and restart
circleci runner token create your-org/mac-runner mac-mini-m4
resource_class tidak ditemukan atau job macet dalam antrean
Jika job tetap dalam antrean dengan "No matching runner found", resource class di konfigurasi Anda tidak cocok dengan dasbor:
# Verify the exact resource class name in the CircleCI web app:
# Runners (sidebar) lists your resource classes
# The resource_class in .circleci/config.yml must match exactly:
# resource_class: your-org/mac-runner (case-sensitive!)
# Check your runner is online:
circleci runner instance list your-org/mac-runner
# Common mistakes:
# - Wrong namespace (org name vs personal namespace)
# - Typo in resource class name
# - Runner is offline or token is invalid
# - Using 'docker' executor instead of 'machine: true'
Kesalahan code signing Xcode
Code signing di mesin CI memerlukan pengelolaan keychain yang cermat. Proses runner mungkin tidak memiliki akses ke login keychain:
# Option 1: Use Fastlane Match (recommended)
# In your Fastfile:
match(type: "appstore", readonly: true)
# Option 2: Manual keychain management in your job steps
- run:
name: Setup code signing
command: |
# Decode and import the certificate
echo "$BUILD_CERTIFICATE_BASE64" | base64 --decode > /tmp/cert.p12
# Create a temporary keychain
security create-keychain -p "ci" /tmp/ci.keychain
security set-keychain-settings -lut 21600 /tmp/ci.keychain
security unlock-keychain -p "ci" /tmp/ci.keychain
# Import the certificate
security import /tmp/cert.p12 -P "$P12_PASSWORD" \
-A -t cert -f pkcs12 -k /tmp/ci.keychain
# Let codesign use the key without a GUI prompt
security set-key-partition-list -S apple-tool:,apple:,codesign: \
-s -k "ci" /tmp/ci.keychain
security list-keychain -d user -s /tmp/ci.keychain
# Install the provisioning profile (Xcode 16 and later location)
PROFILES=~/Library/Developer/Xcode/UserData/Provisioning\ Profiles
mkdir -p "$PROFILES"
echo "$PROVISIONING_PROFILE_BASE64" | base64 --decode \
> "$PROFILES/profile.mobileprovision"
Simulator gagal boot atau time out
Simulator dapat mengalami kondisi buruk pada mesin CI yang berjalan lama. Reset di antara build:
# Add a pre-build step to clean simulators
- run:
name: Reset simulators
command: |
xcrun simctl shutdown all 2>/dev/null || true
xcrun simctl erase all 2>/dev/null || true
# If a specific runtime is missing, reinstall it:
xcodebuild -downloadPlatform iOS
# List available simulators
xcrun simctl list devices available
Ruang disk menipis
Build Xcode menghasilkan data dalam jumlah besar. Siapkan pembersihan otomatis pada runner Anda:
# Check available disk space
df -h /
# Clean DerivedData folders unused for a week
find ~/DerivedData ~/Library/Developer/Xcode/DerivedData \
-mindepth 1 -maxdepth 1 -mtime +7 -exec rm -rf {} +
# Delete simulators the current Xcode no longer supports
xcrun simctl delete unavailable
# List simulator runtimes, then delete the ones you no longer test on
xcrun simctl runtime list
# xcrun simctl runtime delete <runtime-identifier>
# Clean Homebrew cache
brew cleanup --prune=7
# Set up a weekly cron job for automatic cleanup
(crontab -l 2>/dev/null; echo "0 4 * * 0 find ~/DerivedData -mindepth 1 -maxdepth 1 -mtime +7 -exec rm -rf {} + ; /opt/homebrew/bin/brew cleanup --prune=7") | crontab -