एक सेल्फ-होस्टेड Mac रनर क्यों उपयोग करें?
GitHub-होस्टेड macOS रनर सुविधाजनक हैं, लेकिन उनका बिल प्रति मिनट बनता है, इसलिए बिल्ड की मात्रा बढ़ने के साथ लागत भी बढ़ती है। $0.062 प्रति मिनट पर (अक्टूबर 2026 तक GitHub के स्टैंडर्ड macOS रनर की दर), प्रति माह 75 घंटे बिल्ड चलाने वाली एक टीम लगभग $279 का भुगतान करती है। GitHub सेल्फ-होस्टेड रनर के मिनटों का बिल नहीं बनाता, इसलिए My Remote Mac का एक समर्पित Mac mini M4 बिना प्रति मिनट बिलिंग के $85/माह (USD, कर अतिरिक्त) की तय दर पर मिलता है, जो आपको लागत के एक अंश में वही (या बेहतर) हार्डवेयर देता है।
| विशेषता | My Remote Mac सेल्फ-होस्टेड | GitHub-होस्टेड रनर |
|---|---|---|
| लागत | $85/माह (प्रति मिनट बिलिंग नहीं) | $0.062/मिनट (75h के लिए ~$279/माह) |
| आर्किटेक्चर | Apple M4, भौतिक Mac, 1:1 आवंटित | Apple M1 VM, 3 कोर, 7 GB RAM (स्टैंडर्ड रनर) |
| बिल्ड गति | ~4 मिनट (समान प्रोजेक्ट) | ~12 मिनट (मध्यम प्रोजेक्ट) |
| स्थायी कैश | हाँ (स्थायी डिस्क) | नहीं (एफ़ेमरल) |
| कस्टम सॉफ़्टवेयर | पूर्ण रूट एक्सेस | सीमित |
| समवर्ती जॉब्स | प्रति रनर 1 जॉब; स्केल करने के लिए रनर या Mac जोड़ें | 5 (Free, Pro, Team) / 50 (Enterprise) |
My Remote Mac की कीमतें USD में, कर अतिरिक्त। GitHub के आंकड़े: स्टैंडर्ड macOS रनर $0.062 प्रति मिनट, GitHub के प्राइसिंग दस्तावेज़ के अनुसार, अक्टूबर 2026 तक।
मुख्य लाभ: चूँकि रनर स्थायी होता है, 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 इंस्टॉल करें
सबसे पहले, SSH के ज़रिए अपने Mac Mini M4 से कनेक्ट करें। जब आपने अपना 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
अब सेवा हर बार रनर user के लॉगिन पर शुरू होती है और प्रोसेस रुकने पर रनर को पुनः शुरू करती है। LaunchAgent बूट पर अपने आप शुरू नहीं होता: हेडलेस Mac पर, उस user के लिए Automatic login सक्षम करें (System Settings > Users & Groups; FileVault चालू रहते उपलब्ध नहीं), ताकि रीबूट के बाद रनर फिर से ऑनलाइन आ जाए। आप सत्यापित कर सकते हैं कि रनर आपके GitHub रिपॉज़िटरी के 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: प्रदर्शन को ऑप्टिमाइज़ करें
एक सेल्फ-होस्टेड रनर के सबसे बड़े लाभों में से एक स्थायी कैशिंग है। अपने 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/माह (2x M4 Pro) | 5000+ बिल्ड | $3,100+/माह | $2,642+/माह |
My Remote Mac की कीमतें USD में, कर अतिरिक्त। GitHub के आंकड़े: स्टैंडर्ड macOS रनर $0.062 प्रति मिनट, GitHub के प्राइसिंग दस्तावेज़ के अनुसार, अक्टूबर 2026 तक।
निष्कर्ष: प्रति माह ~140 से अधिक बिल्ड चलाने वाली टीमों के लिए, एक सेल्फ-होस्टेड Mac Mini M4 तुरंत अपनी लागत वसूल कर लेता है। और चूँकि वार्म कैश वाले स्थायी हार्डवेयर पर बिल्ड तेज़ होते हैं, आपकी टीम डेवलपर समय भी बचाती है। प्रति माह 100 बिल्ड पर, जैसा कि एकल डेवलपर वाली पंक्ति में है, GitHub-होस्टेड रनर $23/माह सस्ते पड़ते हैं; फिर भी एकल डेवलपर स्थायी कैश और तेज़ बिल्ड के लिए, और इसलिए कि इसमें प्रति मिनट बिलिंग नहीं होती, समर्पित Mac को प्राथमिकता दे सकता है। अगर आप कभी-कभार ही रिलीज़ करते हैं, तो दैनिक प्लान आपको रिलीज़ के दिन के लिए $14 प्रति 24 घंटे से (USD, कर अतिरिक्त) एक Mac देता है: जब तक आप ऑटो-रिन्यू चालू न करें, यह 24 घंटे बाद रुक जाता है, और आप उसी Mac पर मासिक प्लान पर स्विच कर सकते हैं।