Self-hosted macOS build runners for Xcode projects
For development teams that need unattended, repeatable iOS and macOS builds. You register a dedicated Mac mini as a runner in your CI system, and every push produces a signed build or test result on hardware nobody else uses.
- 01
Repository
Push or pull request
- 02
Dedicated Mac runner
Self-hosted agent
- 03
Build & test
xcodebuild, simulators
- 04
Artifact
.ipa, logs, reports
What this solves
Queue time on hosted macOS runners
Shared macOS runners can be slow to start and are billed per minute. A dedicated runner is always on and picks up jobs immediately.
Cold caches on every job
Ephemeral runners start empty. A persistent Mac keeps DerivedData, SwiftPM, CocoaPods and Homebrew caches between builds.
Control over Xcode versions
You decide when to install a new Xcode or macOS release, instead of following the image schedule of a CI vendor.
Example workflow
From pull request to TestFlight
- 1
A developer opens a pull request on GitHub.
- 2
The workflow targets the label self-hosted, macOS, ARM64 and lands on your vBoxx Mac.
- 3
xcodebuild resolves packages from the warm local cache, builds and runs unit tests.
- 4
On merge to main, fastlane signs the app with certificates from the runner keychain and uploads the build to App Store Connect.
- 5
Logs and the .ipa are stored as artifacts in your CI system.
What runs on the Mac, and what does not
Keep the Mac runner for Apple-only steps. Everything else can stay on the Linux runners you already have.
| Pipeline step | Runs on | Why |
|---|---|---|
| Xcode build, unit tests and UI tests | Runs onMac runner | Whyxcodebuild and the iOS Simulator need macOS. |
| Code signing, notarisation and upload to App Store Connect | Runs onMac runner | WhyUses the runner keychain and Apple tools such as notarytool or fastlane. |
| Docker builds, container actions and service containers | Runs onLinux runner | WhyDocker container actions do not run natively on a macOS runner. |
| Linting, backend tests and web builds | Runs onEither | WhyRunning them on Linux keeps the Mac free for Apple jobs. |
How to set it up and run it
- 1
Order a Mac and choose the macOS version in the configurator
You get full admin access over SSH.
- 2
Install Xcode, the command line tools and your toolchain (Homebrew, fastlane, Ruby or Node as needed).
- 3
Create a dedicated, non-admin macOS user for the runner and install the GitHub Actions runner or GitLab Runner (shell executor) as a launch service so it starts after a reboot.
- 4
Import signing certificates into a separate keychain for that user, or fetch them per job with fastlane match.
- 5
Decide concurrency
One runner process per Mac is the safest default for Xcode builds. Add a second runner only after you have measured CPU, memory and disk headroom.
- 6
Plan maintenance
Clean old DerivedData and simulators on a schedule, and test new Xcode releases on a branch before switching the default.
Which configuration fits
Good fit for
A single runner for small to medium apps with one build at a time.
Mac mini M6
- 12-core CPU · 12-core GPU
- Memory: 16 GB
- Storage: 512 GB SSD
Who does what
vBoxx provides
- A dedicated Mac mini M6 or M5 Pro used only by your runner, with the macOS version you choose
- Persistent SSD storage, so build caches survive between jobs and reboots
- Network: 10 Gigabit Ethernet port · 1 Gbit/s unmetered · public IPv4 + IPv6
- Remote APC power control to hard-reboot a runner that hangs, included
- Optional JetKVM console for when macOS no longer responds over SSH
You manage
- Installing and updating Xcode, macOS and the runner agent
- Runner registration, labels, tokens and job concurrency
- Signing certificates, provisioning profiles and secrets
- Firewall rules, SSH keys and access for your team
- Backups of anything that is not stored in Git or your CI system
Before you choose this setup
- Self-hosted runners on public repositories can execute code from forked pull requests. Use them for private repositories, or require approval for outside contributors.
- One Mac is one runner pool. If you need many short parallel jobs with clean machines every time, a hosted ephemeral runner service may fit better.
- There is no managed CI service or automatic Xcode updating included. The Mac is yours to configure.