2026 Enterprise Mac CI resource pools: concurrent codesign, keychain and provisioning profile isolation

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.

Rule of thumb: If two jobs can touch the same keychain file or the same provisioning profile filename, assume they eventually will—under load, not on your laptop.

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

  1. Inventory cert and profile locations (keychains, Library paths, Fastlane).
  2. Canary per-job keychains; wall-clock impact is usually small next to compile.
  3. Move profiles workspace-local; CI-fail on duplicate UUIDs.
  4. Unique OBJROOT/SYMROOT/DSTROOT per job in orchestrator templates.
  5. 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 HOME or 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.