1. Warum einen Self-Hosted Mac Runner verwenden?
GitHub-gehostete macOS-Runner sind praktisch, aber teuer. Bei 0,08 $ pro Minute zahlt ein Team, das 75 Stunden Builds pro Monat ausführt, rund 360 $. Ein dedizierter Mac Mini M4 von MyRemoteMac kostet 85 $/Monat mit unbegrenzten Build-Minuten und bietet Ihnen die gleiche (oder bessere) Hardware zu einem Bruchteil der Kosten.
| Merkmal | GitHub-gehosteter Runner | MyRemoteMac Self-Hosted |
|---|---|---|
| Kosten | 0,08 $/Min. (~350 $/Monat für 75 Std.) | 85 $/Monat (unbegrenzte Minuten) |
| Architektur | Intel x86 (teilweise M1) | Apple M4 (neueste) |
| Build-Geschwindigkeit | ~12 Min. (mittleres Projekt) | ~4 Min. (gleiches Projekt) |
| Persistenter Cache | Nein (flüchtig) | Ja (persistente Festplatte) |
| Individuelle Software | Eingeschränkt | Voller Root-Zugriff |
| Gleichzeitige Jobs | 5 (kostenlos) / 20 (kostenpflichtig) | Unbegrenzt (Ihre Hardware) |
Wichtigster Vorteil: Da der Runner persistent ist, bleiben DerivedData, SPM-Caches und CocoaPods zwischen Builds erhalten. Allein das kann die Build-Zeiten um 50-70 % gegenüber flüchtigen GitHub-gehosteten Runnern verkürzen, die jedes Mal von vorn beginnen.
2. Voraussetzungen
Bevor Sie beginnen, stellen Sie sicher, dass Sie Folgendes haben:
- Einen Mac Mini M4 Server von MyRemoteMac (ab 85 $/Monat)
- Ein GitHub-Konto mit Admin-Zugriff auf Ihr Repository oder Ihre Organisation
- SSH-Zugriff auf Ihren Mac Mini (im MyRemoteMac-Abonnement enthalten)
- Ein Apple Developer-Konto (für Code-Signierung und Provisioning-Profile)
- Grundkenntnisse in YAML und Terminal-Befehlen
3. Schritt 1: Per SSH mit Ihrem Mac Mini verbinden und Xcode installieren
Verbinden Sie sich zunächst per SSH mit Ihrem Mac Mini M4. Sie haben Ihre Zugangsdaten bei der Einrichtung Ihres MyRemoteMac-Servers erhalten.
Per SSH verbinden
# 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
Xcode Command Line Tools installieren
# Install Command Line Tools
xcode-select --install
# Accept the license agreement
sudo xcodebuild -license accept
# Verify installation
xcode-select -p
# /Library/Developer/CommandLineTools
Xcode installieren (Vollversion)
Für iOS-Builds benötigen Sie die vollständige Xcode-Anwendung. Der schnellste Weg, sie auf einem Headless-Server zu installieren, ist mit 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
iOS-Simulatoren installieren
# 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. Schritt 2: Den GitHub Actions Runner installieren
Laden wir nun den GitHub Actions Runner-Agenten herunter und konfigurieren ihn. Navigieren Sie zu Ihrem Repository auf GitHub, gehen Sie zu Settings > Actions > Runners > New self-hosted runner und wählen Sie macOS + ARM64.
Herunterladen und konfigurieren
# 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
Als persistenten launchd-Dienst installieren
Den Agenten interaktiv auszuführen, ist zum Testen in Ordnung, aber für den Produktivbetrieb muss er automatisch beim Booten starten und nach einem Absturz neu starten. GitHub stellt ein integriertes Dienst-Installationsskript für macOS bereit:
# 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
Der Dienst startet nun automatisch beim Booten und startet neu, wenn der Prozess abstürzt. Sie können überprüfen, ob der Runner als "Idle" auf der Seite Settings > Actions > Runners Ihres GitHub-Repositorys erscheint.
Runner für mehrere Repositories konfigurieren (Organisationsebene)
# 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. Schritt 3: Ihren iOS-Build-Workflow erstellen
Erstellen Sie eine Workflow-Datei in Ihrem Repository unter .github/workflows/ios-build.yml. Dieser Workflow läuft auf Ihrem Self-Hosted Mac Mini M4 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
Code-Signierung für Release-Builds hinzufügen
Für TestFlight- oder App-Store-Deployments fügen Sie Schritte zur Code-Signierung hinzu. Speichern Sie Ihre Zertifikate und Provisioning-Profile als 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. Schritt 4: Performance optimieren
Einer der größten Vorteile eines Self-Hosted Runners ist persistentes Caching. Hier sind die wichtigsten Optimierungen, um das Maximum aus Ihrem Mac Mini M4 herauszuholen.
DerivedData-Caching aktivieren
Da der Runner persistent ist, bleibt DerivedData zwischen Builds erhalten. Verwenden Sie einen konsistenten DerivedData-Pfad:
# 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 -
SPM-Pakete cachen
# 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
Parallele Testausführung
# 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
xcbeautify für bessere Logs installieren
# xcbeautify formats Xcode output for CI environments
brew install xcbeautify
# Use it by piping xcodebuild output:
xcodebuild build -scheme "MyApp" | xcbeautify
7. Häufige Probleme beheben
Runner wird in GitHub als "Offline" angezeigt
Das bedeutet in der Regel, dass der launchd-Dienst nicht läuft. Überprüfen Sie den Dienststatus und die 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
Code-Signierung schlägt fehl mit "No signing certificate"
Der launchd-Dienst läuft unter einem anderen Benutzerkontext. Stellen Sie sicher, dass der Schlüsselbund zugänglich ist:
# 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 startet nicht
Simulatoren bleiben manchmal hängen. Setzen Sie sie zwischen Builds zurück:
# 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"
Speicherplatz wird knapp
Xcode-Builds erzeugen viele Daten. Richten Sie eine automatische Bereinigung ein:
# 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. Kostenanalyse
Hier ist ein detaillierter Kostenvergleich für verschiedene Teamgrößen und Build-Volumina:
| Teamgröße | Builds/Monat | GitHub-gehostete Kosten | MyRemoteMac Kosten | Monatliche Ersparnis |
|---|---|---|---|---|
| Einzelentwickler | 100 Builds (Ø 10 Min.) | 80 $/Monat | 85 $/Monat | 5 $/Monat |
| Kleines Team (5) | 500 Builds (Ø 10 Min.) | 400 $/Monat | 85 $/Monat | 325 $/Monat |
| Mittleres Team (15) | 1500 Builds (Ø 10 Min.) | 1.200 $/Monat | 229 $/Monat (M4 Pro) | 971 $/Monat |
| Enterprise (50+) | 5000+ Builds | 4.000+ $/Monat | 458 $/Monat (2x M4 Pro) | 3.542+ $/Monat |
Fazit: Für Teams, die mehr als ~100 Builds pro Monat ausführen, amortisiert sich ein Self-Hosted Mac Mini M4 sofort. Und weil Builds auf persistenter Hardware mit warmen Caches schneller sind, spart Ihr Team auch Entwicklerzeit.
Verwandte Anleitungen
Jenkins Mac Build Agent
Konfigurieren Sie Jenkins-Build-Agenten auf einem dedizierten Mac Mini für iOS CI/CD.
Fastlane + dedizierter Mac-Server
Automatisieren Sie iOS-Builds, Screenshots und TestFlight-Deployment mit Fastlane.
GitLab CI Mac Runner
Registrieren Sie einen GitLab Runner auf einem dedizierten Mac Mini für iOS/macOS-CI-Pipelines.