When several CI jobs codesign on the same Mac, failures look like Keychain races, duplicate provisioning profiles, or two pipelines writing the same DerivedData prefix. Enterprise pools mix long-lived runners with bursty PR queues, so the design question is whether you can sign in parallel without sharing mutable trust state. Below: keychain scope, profile layout, workspace roots, rollout, and a short FAQ. For pool depth and disk-cache races, see also
the enterprise Mac CI resource pool overview
and, for disk races outside signing,
the multi-runner Actions cache and persistent disk FAQ.
1. Failure modes you are really designing against
Typical breaks: duplicate imports into the login keychain, the same profile UUID installed twice, scripts assuming one HOME, or one job deleting signing temps while another archives. Interactive prompts are especially painful under automation because they stall the queue until something times out. Fix shape: partition secrets and mutable trees by job id, disable UI prompts for unattended flows where policy allows, and delete explicitly so keys never linger across tenants or between PR and release lanes.
2. Keychain strategies: shared login versus per-job keychains
Shared login keychains work until concurrency forces global signing locks. Per-job keychains (path per RUN_ID, unlocked for the job, deleted in finally) keep identities disjoint and give auditors a crisp story: the artifact on disk existed only for that pipeline. Middle ground: one keychain per service account on static hardware plus low concurrency per account, rotating certificates on a calendar instead of every commit. Always document whether GUI and SSH may share a keychain—mixed debugging on production runners reintroduces races fast—and consider splitting human-interactive hosts from unattended CI hosts when budgets allow.
3. Provisioning profiles: filesystem layout and collisions
Profiles are UUID-keyed files; duplicates with different entitlements cause intermittent codesign errors. Prefer workspace-local installs and explicit specifiers in project settings instead of silently filling ~/Library/MobileDevice/Provisioning Profiles. If you must use the global folder, isolate copies per job. Track profile lines against bundle ids and capabilities so owners stay obvious.
4. Workspace isolation beyond signing
Also isolate DerivedData, SwiftPM and CocoaPods caches, and notarization staging—e.g. one root per job such as /Volumes/builds/<job>. Point TMPDIR there too. Reuse runners with a post-job scrub of workspace and job keychains; keep warm caches on volumes keyed by Xcode version so entitlements never leak between branches.
5. FAQ: pattern comparison at a glance
Q: One Apple ID keychain for many repos? Possible but coupled; inject secrets as files and import into per-job keychains so blast radius follows the job boundary.
Q: Ephemeral VMs vs static minis? VMs reset cleanly; static hosts stay warm for incremental builds—pair them with scrubs and never reuse keychains across compliance tiers.
Q: Match decrypt location? Job workspace plus throwaway keychain only—not the shared login keychain.
Q: Split App Store vs enterprise signing? Prefer separate accounts or hosts when policies differ so export profiles never cross-pollinate archives.
Q: Notarization and stapling with parallel jobs? Serialize per-team notary credentials or isolate API keys the same way you isolate signing identities; treat upload tokens as secrets with the same per-job hygiene.
6. Rollout steps that survive security review
- Inventory cert and profile locations (keychains, Library paths, Fastlane).
- Canary per-job keychains; wall-clock impact is usually small next to compile.
- Move profiles workspace-local; CI-fail on duplicate UUIDs.
- Unique
OBJROOT/SYMROOT/DSTROOTper job in orchestrator templates. - Document revocation: which identity each pool holds and how to drain hosts safely.
7. Operator checklist before raising concurrency
- Does each job create and delete its own keychain file on a deterministic path?
- Are provisioning profiles version-pinned and installed without clobbering parallel UUIDs?
- Is
HOMEor at least signing-related env vars scoped per job, not per machine default? - Do scrub scripts remove private keys from disk even when jobs fail mid-flight?
- Have you tested two simultaneous archives on the same runner class—not only sequential builds?
Why genuine Mac mini hardware still wins for signing-heavy pools
Apple’s signing stack expects supported Mac hardware and firmware—what Mac mini nodes provide out of the box—plus strong per-watt CPU for compile-and-sign. Gatekeeper, SIP, and disk encryption options simplify security reviews versus ad-hoc PC farms, and Apple Silicon idle power stays low enough to keep warm pools economical.
Standardise on Mac mini M4-class hosts with the isolation rules above, scale out before oversubscribing one login session, and use the Macstripe home page to line up regions and models with your compliance tier.