macOS CI on GitHub Actions can feel painfully slow — is that normal?
On hosted macos-latest, yes — and the pain usually hits in the queue before the CPU. Monday-morning pushes sitting in "Queued" for 20–40 minutes are routine on iOS teams.
This is L1 in the series: starting from how bad the queue really is, we compare self-hosted runner vs macos-latest on queues, CI cost, and xcodebuild, and when a Cloud Mac self-hosted runner is worth it. Why the execution layer exists: why GitHub Runner is the execution engine.
Featured answer
Slow macOS CI on hosted GitHub Actions runners is usually normal — the biggest bottleneck is often the build queue, not Apple Silicon compute.
- Hosted
macos-latest: 20–40 min queue time at peak is common; wall-clock time swings a lot - Cloud Mac self-hosted runner: dedicated node, little to no queue, more stable
xcodebuild - Worth it? Depends on macOS CI frequency — <5 jobs/week and you can tolerate queue → stay hosted; daily iOS CI and queue fatigue → consider self-hosted
Clicked in with one of these questions?
If a specific doubt brought you here, skip the full read — jump straight to the section:
- Is a GitHub Actions self-hosted runner on macOS worth it? → real Cloud Mac TCO + decision tree
- Will CI run faster on Cloud Mac? → queue hurts more than CPU
- Why is macos-latest always queued? → why iOS CI stalls
- Is self-hosted runner free CI? → myth: self-hosted ≠ free
- Why is iOS CI build time so inconsistent? → performance gap + comparison table
Three terms, aligned
macOS CI = jobs needing Xcode / Apple Silicon · Cloud Mac = always-on macOS host (L0) · self-hosted runner = process on that Mac that picks up GitHub Actions jobs (L1)
Renting Cloud Mac does not create a runner; installing a runner does not make CI free — you are swapping GitHub Actions billing for Cloud Mac rent + ops.
self-hosted runner vs macos-latest: three macOS CI options
Performance = wall-clock time and queue time stability, not Geekbench:
| Dimension | Hosted macos-latest |
Cloud Mac self-hosted runner | Local Mac self-hosted runner |
|---|---|---|---|
| CI cost model | Per-minute (GitHub Actions billing) | Cloud Mac monthly + ops | Hardware + power |
| build queue / wait | Often 20–40+ min at peak | Dedicated node, little queue | No queue (if online) |
| xcodebuild stability | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Apple Silicon CI | Shared pool, fresh image each run | Fixed Xcode / certs / DerivedData | Same as Cloud Mac |
| Best for | Low-frequency archive, OK with queue | Daily iOS CI, hate waiting | Mac mini already on 24/7 |
Why the biggest macOS CI cost is queue time, not CPU
You open Actions logs, see xcodebuild ran 18 minutes, and blame the machine. The real damage is often before that line — job status stuck on Queued.
For iOS teams, wall-clock = queue time + build time + retries. Hosted macos-latest queue at weekday peaks is unpredictable; you pay macOS minutes, but time waiting for a green PR does not show on the invoice — yet it burns velocity.
So split "CI is slow": queue slow or compile slow? Queue → self-hosted runner (Cloud Mac or local) helps most; compile → tune Xcode, cache, deps.
Why iOS CI on GitHub Actions keeps "stuck in queue"
macos-latest runs in GitHub's shared macOS runner pool — limited supply, concentrated demand:
- iOS CI must be macOS: unlike Node repos on
ubuntu-latest, every Apple job hits the same pool - Peak stacking: US/EU mornings plus Asia afternoons — global pushes collide
- Concurrency still queues: three macOS jobs in one workflow is often 3× wait, not 3× speed
- Minutes still bill: after the queue, every
xcodebuildminute counts toward GitHub Actions billing
Classic feel: PR reviewed, Checks still yellow — not bad code, no runner yet. That is what "normal" means here: on the hosted pool, slowness is structural — not misconfiguration.
Two common misreads
Misread ①: "We have Cloud Mac, so CI is fixed"
Cloud Mac gives you always-on macOS. Claude Code over SSH and Linux Actions tests can run in parallel — hence "Agent happy in terminal, xcodebuild red in PR." To kill the queue, register a self-hosted runner and fix runs-on labels.
Misread ②: "self-hosted runner = free CI"
GitHub does not bill per-minute for self-hosted, but machine rent, Cloud Mac monthly, certs, and on-call are yours. You moved cost from GitHub to your own TCO — nobody gets free macOS CI.
Real TCO for a Cloud Mac self-hosted runner
Do not compare sticker prices. Count: macOS minutes × rate + human wait from queue + ops time.
① Hosted macos-latest
Billed on actual run minutes (macOS rates are much higher than Linux — check GitHub's current table). Zero ops, but build queue is uncontrollable.
Quick check: repo Actions → Billing, last 30 days of macOS CI minutes. Under ~200/month and occasional queue is OK → hosted is often simplest.
② Cloud Mac + self-hosted runner
Cost = Cloud Mac monthly + setup time (~1–2 hours) + workspace cleanup. You buy near-zero queue time on a dedicated Apple Silicon CI box.
Rule of thumb (aligned with L1-Q01): ≥10 macOS jobs/week and queue fatigue → fixed Cloud Mac TCO often beats "hosted minutes + wait tax". Pricing: plan details.
③ Local Mac mini + self-hosted runner
Cost = hardware + power + guaranteed 24/7 uptime. See Mac mini vs Cloud Mac: if you already need a Mac for iOS dev, hanging a runner is natural; buying a Mac only for CI → many teams pick Cloud Mac instead.
Hosted monthly ≈ macOS CI minutes × per-minute rate + human wait from queue Cloud Mac monthly ≈ rent + light ops (stable queue time) When hosted monthly keeps exceeding Cloud Mac rent → consider self-hosted (cloud or local)
self-hosted runner vs macos-latest: queue and xcodebuild performance
Self-hosted is not always faster CPU — but wall-clock is usually shorter and steadier because the queue is gone:
- queue time: shared hosted pool vs dedicated self-hosted — biggest gap here
- xcodebuild: consistent cold start and disk state → lower variance in iOS CI build time
- concurrency: one self-hosted runner defaults to one job; hosted can run more jobs, but macOS jobs still queue
- environment: pin Xcode, keychain, DerivedData; hosted may be clean but unfamiliar every run
If Runner + Ollama / Claude Code cause Swap, see AI workload scheduling — sometimes "slow" is memory, not queue.
When Cloud Mac self-hosted runner is worth it
- At least one iOS CI workflow per day and build queue fatigue
- Workflows with
xcodebuild, signing, notarize, TestFlight - Want green/red Checks within 5–15 minutes of push (Facts on time)
- Run Claude Code + CI on the same Cloud Mac — fewer "SSH green, Actions red" splits
At that point the question is not "self-hosted or not" but Cloud Mac or local Mac for the runner.
When staying on macos-latest is smarter
- Archive once or twice a month, OK with occasional queue
- Main repo green on Linux; few macOS jobs
- Still validating iOS — prove pipeline on hosted before a fixed node
- No one to maintain runner upgrades, token rotation, disk cleanup
Same Cloud Mac: Claude Code + Runner — realistic?
Yes. Claude Code by day, heavy xcodebuild off-peak. On 16GB unified memory, Ollama + Runner + Agent together can Swap and slow CI. Series line: Claude Code produces Diff; GitHub Runner produces Fact.
Decision tree: when you cannot tolerate the queue
Does workflow need macOS / Xcode?
↓
No → keep Linux hosted
Yes → Can you accept build queue? (20–40 min)
↓
Yes → stay on macos-latest
No → More than ~10 macOS jobs/month?
↓
No → hosted OK; watch Billing
Yes → Need 24/7 online?
↓
Yes → Cloud Mac self-hosted runner
No → local Mac self-hosted (keep it awake)
Practical path: keep hosted, rent Cloud Mac for two weeks, compare queue time, GitHub Actions billing, and xcodebuild wall-clock.
FAQ
Is slow macOS CI on GitHub Actions normal?
On hosted macos-latest, yes. Slowness often starts in the queue; 20–40 minutes at peak is common.
Why is macos-latest always queued?
Shared macOS runner pool with limited capacity; iOS CI has no Linux escape hatch; global peaks stack.
Is a GitHub Actions self-hosted runner on macOS worth it?
Usually yes for daily iOS CI and low queue tolerance; occasional macOS jobs → stay hosted.
Will CI run faster on Cloud Mac?
Not always faster CPU, but little queue and more stable xcodebuild on Apple Silicon.
Is self-hosted runner free CI?
No. GitHub skips per-minute fees; Cloud Mac rent and ops are yours.
Why is iOS CI build time so inconsistent?
Queue swings plus fresh hosted images; self-hosted can pin environment and cache.
Can Claude Code share the same machine?
Yes, but stagger workloads. See scheduling guide.
Slow macOS CI is not always a broken pipeline — see whether it is queue or build, then decide on a Cloud Mac self-hosted runner.
Tired of the build queue?
Rent Cloud Mac — compare two weeks of queue time and billing
Dedicated Apple Silicon macOS for a self-hosted GitHub Actions runner — pull iOS CI out of the macos-latest queue. Compare GitHub Actions billing and xcodebuild wall-clock before committing to a fixed node.
