1. ¿Por qué usar un Self-Hosted Mac Runner?
Los runners macOS alojados en GitHub son convenientes pero costosos. A $0.08 por minuto, un equipo que ejecuta 75 horas de compilaciones al mes paga alrededor de $360. Un Mac Mini M4 dedicado de MyRemoteMac cuesta $75/mes con minutos de compilación ilimitados, dándole el mismo (o mejor) hardware por una fracción del coste.
| Característica | Runner alojado en GitHub | MyRemoteMac Self-Hosted |
|---|---|---|
| Coste | $0.08/min (~$350/mes por 75h) | $75/mes (minutos ilimitados) |
| Arquitectura | Intel x86 (algunos M1) | Apple M4 (último modelo) |
| Velocidad de compilación | ~12 min (proyecto mediano) | ~4 min (mismo proyecto) |
| Caché persistente | No (efímero) | Sí (disco persistente) |
| Software personalizado | Limitado | Acceso root completo |
| Jobs concurrentes | 5 (gratis) / 20 (pago) | Ilimitados (su hardware) |
Beneficio clave: Debido a que el runner es persistente, DerivedData, las cachés de SPM y CocoaPods se preservan entre compilaciones. Esto por sí solo puede reducir los tiempos de compilación en un 50-70% comparado con los runners efímeros alojados en GitHub que comienzan desde cero cada vez.
2. Requisitos previos
Antes de comenzar, asegúrese de tener lo siguiente:
- Un servidor Mac Mini M4 de MyRemoteMac (desde $75/mes)
- Una cuenta de GitHub con acceso de administrador a su repositorio u organización
- Acceso SSH a su Mac Mini (incluido con su suscripción de MyRemoteMac)
- Una cuenta de Apple Developer (para la firma de código y perfiles de aprovisionamiento)
- Familiaridad básica con YAML y comandos de terminal
3. Paso 1: Conectarse por SSH al Mac Mini e instalar Xcode
Primero, conéctese a su Mac Mini M4 por SSH. Habrá recibido sus credenciales al configurar su servidor MyRemoteMac.
Conectarse por SSH
# Connect to your Mac Mini M4
ssh admin@your-server-ip
# Verify you're on Apple Silicon
uname -m
# Expected output: arm64
# Check macOS version
sw_vers
# ProductName: macOS
# ProductVersion: 15.2
# BuildVersion: 24C101
Instalar 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
Instalar Xcode (versión completa)
Para compilaciones iOS, necesita la aplicación Xcode completa. La forma más rápida de instalarla en un servidor headless es usando 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 stable Xcode
xcodes install 16.2
# Set it as the active Xcode
sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
# Verify
xcodebuild -version
# Xcode 16.2
# Build version 16C5032a
Instalar simuladores iOS
# Install the iOS 18 simulator runtime
xcodebuild -downloadPlatform iOS
# Verify simulator availability
xcrun simctl list runtimes
# == Runtimes ==
# iOS 18.2 (18.2 - 22C150) - com.apple.CoreSimulator.SimRuntime.iOS-18-2
4. Paso 2: Instalar el GitHub Actions Runner
Ahora vamos a descargar y configurar el agente GitHub Actions runner. Navegue a su repositorio en GitHub, vaya a Settings > Actions > Runners > New self-hosted runner y seleccione macOS + ARM64.
Descargar y configurar
# Create a directory for the runner
mkdir -p ~/actions-runner && cd ~/actions-runner
# Download the latest runner package (ARM64)
curl -o actions-runner-osx-arm64-2.321.0.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.321.0/actions-runner-osx-arm64-2.321.0.tar.gz
# Extract the package
tar xzf actions-runner-osx-arm64-2.321.0.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
Instalar como un servicio launchd persistente
Ejecutar el agente de forma interactiva está bien para pruebas, pero para producción necesita que se inicie automáticamente al arrancar y se reinicie si falla. GitHub proporciona un script de instalación de servicio integrado para macOS:
# Install as a launchd service
cd ~/actions-runner
sudo ./svc.sh install
# Start the service
sudo ./svc.sh start
# Check the service status
sudo ./svc.sh status
# Expected: "active (running)"
# View the launchd plist (for reference)
cat /Library/LaunchDaemons/actions.runner.*.plist
El servicio ahora se iniciará automáticamente al arrancar y se reiniciará si el proceso muere. Puede verificar que el runner aparezca como "Idle" en la página Settings > Actions > Runners de su repositorio de GitHub.
Configurar el Runner para múltiples repositorios (nivel de organización)
# 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
5. Paso 3: Crear su workflow de compilación iOS
Cree un archivo de workflow en su repositorio en .github/workflows/ios-build.yml. Este workflow se ejecutará en su Mac Mini M4 self-hosted runner.
name: iOS Build & Test
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: [self-hosted, macOS, ARM64, M4]
env:
SCHEME: "MyApp"
DESTINATION: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Select Xcode version
run: |
sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
xcodebuild -version
- name: Resolve Swift Package Dependencies
run: |
xcodebuild -resolvePackageDependencies \
-scheme "$SCHEME" \
-clonedSourcePackagesDirPath .spm-cache
- name: Build the app
run: |
xcodebuild build \
-scheme "$SCHEME" \
-destination "$DESTINATION" \
-clonedSourcePackagesDirPath .spm-cache \
-derivedDataPath DerivedData \
| xcbeautify
- name: Run unit tests
run: |
xcodebuild test \
-scheme "$SCHEME" \
-destination "$DESTINATION" \
-clonedSourcePackagesDirPath .spm-cache \
-derivedDataPath DerivedData \
-resultBundlePath TestResults.xcresult \
| xcbeautify
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: TestResults.xcresult
Añadir firma de código para compilaciones de release
Para despliegues en TestFlight o App Store, añada pasos de firma de código. Almacene sus certificados y perfiles de aprovisionamiento como GitHub Secrets:
deploy:
needs: build
runs-on: [self-hosted, macOS, ARM64, M4]
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v4
- 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
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/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 }}
run: |
xcrun altool --upload-app \
-f DerivedData/Export/MyApp.ipa \
-t ios \
--apiKey $APP_STORE_CONNECT_API_KEY
6. Paso 4: Optimizar el rendimiento
Una de las mayores ventajas de un self-hosted runner es la caché persistente. Aquí tiene las optimizaciones clave para sacar el máximo provecho de su Mac Mini M4.
Habilitar caché de DerivedData
Como el runner es persistente, DerivedData se preserva entre compilaciones. Use una ruta de DerivedData consistente:
# In your workflow, always use:
-derivedDataPath DerivedData
# On the runner, periodically clean old DerivedData to save space:
# Add a cron job to clean builds older than 7 days
echo "0 3 * * 0 find ~/actions-runner/_work/*/DerivedData -maxdepth 0 -mtime +7 -exec rm -rf {} +" \
| crontab -
Cachear paquetes SPM
# Use clonedSourcePackagesDirPath to keep SPM packages on disk
xcodebuild build \
-scheme "MyApp" \
-clonedSourcePackagesDirPath ~/spm-cache \
-derivedDataPath DerivedData
# This avoids re-downloading packages on every build
Ejecución paralela de pruebas
# Run tests in parallel across multiple simulators
xcodebuild test \
-scheme "MyApp" \
-destination "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2" \
-destination "platform=iOS Simulator,name=iPhone 15,OS=17.5" \
-parallel-testing-enabled YES \
-maximum-parallel-testing-workers 4 \
-derivedDataPath DerivedData \
| xcbeautify
Instalar xcbeautify para mejores logs
# xcbeautify formats Xcode output for CI environments
brew install xcbeautify
# Use it by piping xcodebuild output:
xcodebuild build -scheme "MyApp" | xcbeautify
7. Solución de problemas comunes
El Runner aparece como "Offline" en GitHub
Esto generalmente significa que el servicio launchd no está ejecutándose. Verifique el estado del servicio y los logs:
# Check service status
sudo ./svc.sh status
# View logs
cat ~/actions-runner/_diag/Runner_*.log | tail -50
# Restart the service
sudo ./svc.sh stop
sudo ./svc.sh start
La firma de código falla con "No signing certificate"
El servicio launchd se ejecuta bajo un contexto de usuario diferente. Asegúrese de que el keychain sea accesible:
# 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
El simulador no arranca
Los simuladores a veces se atascan. Reinícielos entre compilaciones:
# 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 16 Pro"
Espacio en disco insuficiente
Las compilaciones de Xcode generan muchos datos. Configure una limpieza automática:
# Clean old DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData/*
# Remove old simulator runtimes
xcrun simctl runtime delete all
# Clean Homebrew cache
brew cleanup --prune=7
# Remove old Xcode archives
rm -rf ~/Library/Developer/Xcode/Archives/*
8. Análisis de costes
Aquí tiene una comparación detallada de costes para diferentes tamaños de equipo y volúmenes de compilación:
| Tamaño del equipo | Compilaciones/Mes | Coste alojado en GitHub | Coste MyRemoteMac | Ahorro mensual |
|---|---|---|---|---|
| Desarrollador individual | 100 compilaciones (10 min promedio) | $80/mes | $75/mes | $5/mes |
| Equipo pequeño (5) | 500 compilaciones (10 min promedio) | $400/mes | $75/mes | $325/mes |
| Equipo mediano (15) | 1500 compilaciones (10 min promedio) | $1,200/mes | $179/mes (M4 Pro) | $1,021/mes |
| Empresa (50+) | 5000+ compilaciones | $4,000+/mes | $358/mes (2x M4 Pro) | $3,642+/mes |
Conclusión: Para equipos que ejecutan más de ~100 compilaciones por mes, un Mac Mini M4 self-hosted se amortiza de inmediato. Y como las compilaciones son más rápidas en hardware persistente con cachés precargadas, su equipo también ahorra tiempo de desarrollo.
Guías relacionadas
Agente de compilación Jenkins para Mac
Configure agentes de compilación Jenkins en un Mac Mini dedicado para CI/CD de iOS.
Fastlane + Servidor Mac dedicado
Automatice compilaciones iOS, capturas de pantalla y despliegue en TestFlight con Fastlane.
GitLab CI Mac Runner
Registre un GitLab Runner en un Mac Mini dedicado para pipelines CI de iOS/macOS.