Decision: Don’t convert developer headcount directly into a Mac build machine count. Measure job arrivals, execution time, peak concurrency, and acceptable queue time first; then test the estimate with representative CI work. Keep stable, recurring demand on planned capacity and assess flexible capacity for short peaks.
This is for technical leads planning iOS CI capacity from scratch, IT managers investigating runner queues, and procurement teams comparing steady capacity with elastic additions.
If you need a general CI architecture guide or a permissions-only tutorial, this article is not that. It focuses on estimating, testing, and reviewing build capacity using your own run records.
Start with the queue you already have
The number of developers is an indirect clue, not a capacity measurement. A team may submit work in bursts, while jobs vary in duration and resource use. A headcount ratio can therefore suggest too many machines during quiet periods and too few during a release rush.
Begin by collecting records for each CI job. Capture its trigger time, start time, finish time, job type, outcome, and retry history. Calculate queue duration from trigger to start, and execution duration from start to finish. Keep those measures separate: a slow result can come from a long-running job, a crowded runner pool, or both.
Classify work by what it does. Pull request checks, main-branch builds, tests, and archive tasks may have different urgency and resource patterns. Apple’s Xcode workflow documentation describes actions that include building, testing, analyzing, and archiving; use those workflow stages as a prompt for categorizing your own records, rather than assuming every job has the same load profile (Xcode workflow action reference).
A useful baseline answers questions such as:
- Which job types account for most of the waiting time?
- Do waits cluster around workday peaks or release activity?
- Which jobs overlap, and which regularly retry?
- Are long waits caused by unavailable runners, or by a single job that takes a long time to finish?
Keep the raw job events, not just a daily average. Averages can hide the periods when people are waiting for pull request feedback or when release work is competing for runners.
Capacity note: If your CI logs record only total pipeline duration, add timestamps for queue, start, and finish before using that data to justify more machines. Without the queue boundary, you cannot tell whether extra runner capacity addresses the actual delay.
Estimate demand against team service targets
Set a wait target by job purpose before deciding how many runners to provide. A pull request check may need to return quickly enough to keep code review moving; an archive job may follow a different operational target. These are team decisions. There is no universal queue threshold or standard machine count that can be applied without your workload data.
Review at least three kinds of operating conditions in your own records: ordinary periods, recurring busy periods, and release windows. Look at the jobs arriving at the same time and how long each remains active. This reveals whether your demand is mostly steady or concentrated into bursts.
Use a demand worksheet with fields such as:
- Job category: pull request check, branch build, test, archive, or another recorded task.
- Arrival pattern: when jobs are triggered and whether they overlap.
- Execution duration: the observed time from runner start to job completion.
- Queue duration: the observed wait before a runner starts the job.
- Outcome and retries: whether failures or retries add work during busy periods.
- Service target: the maximum wait or completion time your team has agreed to tolerate for that job category.
Do not turn these fields into a universal formula with a fixed multiplier. Instead, state a capacity hypothesis in operational terms: which job types must start promptly, when overlap becomes a problem, and what queue duration would trigger an investigation. Your own data supplies the values.
For Xcode CI, include the real work the pipeline performs. A job that builds a Swift package or an app that uses one may have a different duration from a lightweight validation task. Apple documents this CI workflow use case in its guide to building Swift packages and apps that use them in continuous integration. Use timings from your projects rather than substituting a generic build-time estimate.
Map concurrent jobs to usable Mac runners
A queued job can start only when the CI platform can assign it to an eligible runner that is available and permitted to take that work. So the runner count you plan is not necessarily the count the scheduler can use for every job.
Check the configuration between job demand and runner availability:
- Concurrency controls: Workflow-level limits can constrain how much work runs at once. Review the settings that apply to the workflow and job types you are measuring.
- Runner labels or groups: A job may request a specific runner label or group. If only part of the pool matches, total machine count can overstate capacity for that workload.
- Repository and team access: Access rules can prevent a runner from serving a workflow even when the machine is online.
- Job duration and resource contention: Two jobs may be schedulable at once but still compete for local resources. Verify the combination you intend to run rather than assuming that assignment equals successful throughput.
For example, GitHub Actions documents self-hosted runner management, including runner labels and groups. Its workflow syntax also provides concurrency controls. Treat these as platform-specific settings, not rules that automatically apply to other CI products (self-hosted runner documentation; workflow syntax reference).
Confirm that each important job can reach the runner pool intended for it. A labeling or access mismatch can leave a suitable machine idle while jobs wait in another queue. Record the platform, runner group, labels, and concurrency settings alongside the CI measurements so a later review can distinguish a capacity shortage from a scheduling configuration issue.
Validate the estimate with representative builds
A capacity estimate becomes useful only after you test it with the work that creates the demand. Select representative jobs from your own projects: include common checks as well as the longer or more resource-intensive jobs that influence peak waiting. Run them through the intended workflow and record queue time, execution time, outcomes, retries, and signs of resource contention.
Compare the results with the service targets you set earlier. If queue time misses the target while runners are consistently occupied with eligible work, adding capacity may be appropriate. If runners are idle while work waits, investigate labels, groups, workflow concurrency, or repository access first. If execution time is the problem, examine the job itself and its workload before treating additional machines as the answer.
Keep steady demand distinct from a short-lived surge. A pool that meets ordinary needs may still miss a release-window target; conversely, permanently sizing for an occasional burst may leave machines underused for the rest of the cycle. Your test should show which of those conditions you have, rather than conceal them in one blended average.
| Capacity choice | Best fit | Check before committing | Main trade-off |
|---|---|---|---|
| Fixed runner capacity | Demand is recurring and consistently occupied across the periods that matter | Confirm queue targets are met in ordinary and busy periods, and include purchase or lease, support, maintenance, and idle-time costs in your own estimate | Predictable availability, but peak-sized capacity can sit idle outside busy periods |
| Elastic additions | Baseline demand is steady but bursts are intermittent | Verify that the CI scheduler can use the added runners, that they are available when needed, and that actual usage and rates are known | Can match short peaks more closely, but availability, setup, and usage costs need validation |
| Scheduling or job optimization | Delays trace to configuration, oversized jobs, or avoidable contention | Change one relevant workflow or scheduling condition, then measure the same queue and execution fields again | May improve utilization without new machines, but does not remove a genuine shortage of concurrent capacity |
Use this comparison as a decision aid, not a price or performance claim. Fill in costs from actual quotes and measured use. For runner-specific configuration, the Xcode Cloud workflow reference and your selected CI platform’s current documentation can help identify which scheduling assumptions are platform-dependent.
Common capacity questions
Should the team use developer count to estimate runner count?
Use developer count as context, not as the sizing rule. Compare it with actual job arrivals and overlapping work. A change in team size can prompt a review, but measured queue and execution records show whether that change has affected capacity.
What is a reasonable queue time before adding a runner?
Set a target that reflects the urgency of each job type. Track whether queue time exceeds that target repeatedly during the periods you care about. Before adding a runner, confirm that eligible runners are occupied and that workflow limits or access rules are not holding work back.
Can historical CI records be used when the current setup has few runners?
Yes, but treat them as observations of the current setup, not proof of how a larger pool will behave. Use trigger, start, finish, job category, outcome, and retry records to form a hypothesis, then test it with representative work on the intended runner configuration.
How should a team handle a short release peak?
First check whether the burst is predictable and whether current workflow priorities and concurrency settings are appropriate. If the steady pool meets normal targets but misses release targets, test an elastic addition against that window and record its real availability and cost. Do not permanently size for a brief peak without comparing the resulting idle time.
Keep capacity reviews tied to changes
Revisit the estimate when the workload changes, not just when someone asks for another machine. A new release process, broader test matrix, added project, changed workflow, or drift in job duration can alter runner demand. Maintain a review record with the period examined, job categories, queue targets, platform settings, test results, and the resulting capacity decision.
Use a repeatable review sequence:
- Refresh the job-event data and confirm that queue and execution durations are captured separately.
- Compare the latest ordinary and busy periods with the targets for each job type.
- Check for changes in workflow concurrency, runner groups, labels, or repository access.
- Re-run representative builds when project or test work has changed.
- Record why you chose to add capacity, adjust scheduling, or leave the pool unchanged.
- Recheck fixed and elastic options using current quotes, actual usage assumptions, and the support and maintenance costs relevant to your organization.
If your build jobs rely on Xcode-specific environment variables or action identifiers, verify those details against Apple’s environment variable reference rather than relying on assumptions carried over from another runner setup. Apple’s guide to configuring a first Xcode Cloud workflow is also useful when checking which workflow actions your team actually uses; it does not supply a universal capacity ratio.
The best decision is the one your records can support. Keep predictable, consistently occupied work on planned capacity; investigate configuration and job duration before expanding; and evaluate flexible runners only when measured peaks justify them. For team-specific security and operational concerns, review SDKMac’s data privacy information and reliability details as part of your deployment assessment.
If your measurements show that short peaks—not continuous demand—are the gap, compare the required runner type and availability with SDKMac’s available Mac configurations and rental periods before deciding whether to supplement your setup. Check that the option fits your CI platform, access controls, and workload first. If your runners must stay under your organization’s physical control or require local hardware connections, remote capacity may not be the right addition; if the need is sustained and predictable, compare its full cost with owning and operating that capacity.
Frequently asked questions
How do I estimate the number of Mac build machines for an iOS team?
Start with CI records, not a developer-to-machine ratio. Group jobs by purpose, then compare arrival times, execution duration, peak overlap, queue time, and retry behavior. Turn those observations into a capacity hypothesis, test representative builds on the runners you plan to use, and compare measured queue and execution times with your team’s service targets before adding machines.
Should Mac build capacity be based on developer headcount or build concurrency?
Use build concurrency as the closer operational signal. Headcount may help explain changes in activity, but developers do not submit work at an even rate, and jobs differ in duration and resource use. Measure overlapping jobs and their queue behavior during normal work, release windows, and other busy periods. Use headcount only as context for interpreting those records, not as a conversion formula.
When should a team add another Mac runner because jobs are waiting?
There is no universal queue-time threshold that applies to every team. Set an acceptable wait for each job type based on how quickly developers need feedback and what release work requires. Add or reassign capacity only when repeated measurements show that queue delays exceed those targets during relevant periods, and first check whether scheduling, runner limits, or long-running jobs are causing the delay.
How can historical CI records show whether iOS capacity is sufficient?
Collect job trigger time, start time, finish time, job type, queue duration, outcome, and retry information. Separate pull-request checks from branch builds, tests, and archives so unlike workloads do not distort one another. Review busy periods as well as ordinary operation, then use a representative workload test to see whether the proposed runner pool meets your queue and execution targets.
Further reading
Plan Your iOS CI Capacity with SDKMac
Match your measured build concurrency and memory needs to a dedicated physical Mac node.
