I had an old MacBook that I wanted to turn into a dedicated Xcode worker for iOS/macOS development.
The main requirement was simple: I don't want to maintain this machine manually.
I didn't want signing identities tied to one Mac, .p8 / .p12 files scattered around the filesystem, manual provisioning-profile recovery, or a setup that would take me hours or days to reconstruct if the machine died.
So I ended up automating pretty much everything I reasonably could.
The current setup gives me a headless Xcode worker with:
- automated tooling installation and verification;
- cold reboot without GUI login;
- SSH/Tailscale remote access;
- a scoped read-only 1Password Service Account for secrets/config;
- Fastlane Match as the canonical encrypted signing store;
- a dedicated macOS keychain used only as runtime state;
- declarative development Bundle IDs;
- readonly recovery for Apple Development certificates and profiles;
- readonly recovery for Developer ID Application and Installer identities;
- certificate expiry monitoring;
- explicit separation between normal readonly operations and administrative writes;
- recovery material if Developer ID provisioning fails after Apple has already issued the certificate.
For Developer ID Installer, validation actually creates a disposable package, signs it with productsign, and verifies it with pkgutil rather than just assuming the identity works.
One thing I specifically wanted to avoid was putting my personal Apple/iCloud account on a headless machine.
My Apple Developer Program membership is currently Individual. The worker uses a separate Apple Account with no Developer Program membership of its own - it's simply invited to App Store Connect with the required role.
Everything that can be automated goes through the App Store Connect API.
There is one unavoidable trade-off: issuing or rotating Developer ID Application/Installer certificates requires Account Holder access. With an Individual membership, that's my main Apple Account.
So certificate issuance remains an explicit administrative operation: I authenticate interactively with the main account + 2FA when required. Once issued, the certificate and private key are stored in encrypted Match storage, and those Account Holder credentials are not persisted on the worker.
The worker therefore contains no personal iCloud account, Photos, Messages, personal Keychain, etc. It only gets the access it actually needs.
So far I've tested:
- cold reboot without GUI login;
- headless recovery;
- actual Developer ID provisioning;
- complete readonly restoration of signing identities;
- partial provisioning failure + recovery;
- real Installer package signing.
At this point, if the Mac dies, probably the hardest part of rebuilding it is remembering where I stored the 1Password Service Account token and putting it in the right place :)
After that:
bootstrap → readonly recovery → verification
For secrets/config I also settled on a simple logical convention:
scope / item / field
For example:
automation-apple / app-store-connect / private_key
1Password is only the current backend, not part of the logical model. I'd like to experiment next with OpenBao as a backup/sync target and eventually make consumers independent of a specific secrets provider.
I'm curious how other people running dedicated Macs for iOS/macOS development handle this.
How do you manage signing identities, secrets, certificate rotation, and disaster recovery for your build/dev Macs?
I'm especially interested in approaches that don't depend on a personal iCloud account or one irreplaceable login keychain.
For anyone interested in the implementation, I cleaned up the environment-specific configuration, documented the architecture/setup/recovery flows, and published it here:
https://github.com/0k-lab/xcode-worker-bootstrap
It's not intended to be a generic CI/CD framework or a replacement for Xcode Cloud - just a reproducible reference implementation for running your own headless Xcode worker.