Anleitung - CI/CD

GitHub Actions Self-Hosted Runner auf Mac Mini M4 einrichten

Schritt-für-Schritt-Anleitung zur Einrichtung eines GitHub Actions Self-Hosted Runners auf einem dedizierten Mac Mini M4. Schnellere iOS-Builds, nativ auf Apple Silicon und bis zu 10x günstiger als GitHub-gehostete macOS-Runner.

30 Min. Lesezeit Aktualisiert März 2026

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

Bereit, Ihre iOS-Builds zu beschleunigen?

Setzen Sie einen dedizierten Mac Mini M4 als Ihren GitHub Actions Runner ein. Ab 85 $/Monat.

Mehr Details gesucht?

Durchsuchen Sie die vollständige Dokumentation für Schritt-für-Schritt-Anleitungen, Konfigurationsreferenzen und Fehlerbehebung.

Dokumentation öffnen →