VPSSpark Blog
← Back to Dev Diary

GitHub Actions Keeps Slowing iOS Teams — The Xcode CI Fix That Actually Landed in 2026

Server Notes · Cloud Mac CI #9 · 2026.06.12 · ~12 min read
Field notes: 47 minutes in queue, split pipelines, one Cloud Mac runner, agent guardrails

People search: GitHub Actions iOS slow · Xcode CI queue · self-hosted macOS runner · Cloud Mac GitHub Actions

MacBook with code editor and terminal — an iOS team debugging GitHub Actions Xcode CI
A Thursday afternoon Slack ping — “still Queued” — most iOS teams know this one.

Thursday, May 29, 2026, 3:03 PM. On a twelve-person iOS team, someone in Slack #ios-ci dropped a Run link: "Local Xcode passed in twelve minutes — PR still Queued." Half an hour of silence. Messages like that are background noise. Forty-eight minutes later the same person added: "Queued 47m, run 12m. Don't expect a merge tonight." A release engineer stepped out of a meeting, glanced at their phone: "ios-release Archive is still in line — PR lane's blocked again."

Slack #ios-ci thread: developer reports 47 minutes Queued, release engineer notes Archive blocking PR lane
Fig. 5 · Slack #ios-ci excerpt (repo and org redacted; timestamps and numbers kept)

Screenshots below come from materials a customer shared while we helped triage — redacted, numbers untouched. Cross-check against GitHub's job execution time and queue_wait_seconds docs if you want to reproduce the math yourself.

Two apps, one monorepo, all CI on GitHub-hosted macOS runners. Tolerable in 2025; by spring 2026 the feedback loop had stretched past patience. GitHub Actions wasn't "broken" — for iOS teams it felt more like a shared shuttle that always jams at rush hour.

2832s
queue_wait_seconds
724s
run duration
3.9×
queued / run ratio

That Thursday: open the Job page — yellow bar four times longer than green

They sent a screenshot of Run #18472910356's Job page — not a story, a timestamped record. Yellow bar (Queued): 47m 12s. Green bar (Run): 12m 4s. Footer fields: queue_wait_seconds: 2832, run duration: 724. The math is blunt: wait time >> run time.

GitHub Actions job timeline: Queued 47m12s, Run 12m4s, run 18472910356
Fig. 1 · Run #18472910356 · ios-pr.yml · macos-latest · 2026-05-29 15:04 CST

The team was still in a meeting debating -jobs parallelism and DerivedData cache keys — nobody had pulled queued vs. run columns for the last ten macOS jobs. Release had it worse: ios-release.yml packed xcodebuild archive, notarization, TestFlight upload, and PR unit tests onto one lane, one macos-latest label. Archive runs 25–40 minutes, chewing scarce concurrency slots — fast-feedback PR jobs get dragged into the same queue.

If you only want to triage Queued right now, start with our macOS runner queue runbook. Their turning point began after the room admitted the problem was supply — and numbers, not gut feel, won the argument.

Evidence: a hand-copied sheet of ten failed jobs

Before standup on May 30, someone manually copied ten failed macOS jobs from the Actions run list (below). No fancy BI — just a table with two columns that matter: queued_s and run_s. Median queued 2650s, run 724s; ratio peaked at 4.5×. The room went quiet for a few seconds: nobody brought up "maybe it's Xcode 17.4" again.

Hand-copied queued_s and run_s stats for ten failed macOS jobs
Fig. 3 · Ten failed macOS jobs, 2026-05-22 — 05-29 (redacted)
Often blamed onSignal in Fig. 3More likely cause
GitHub outage10/10 rows queued_s > run_smacOS concurrency cap, heavy jobs hogging slots
Project got biggerrun_s stable at 690–811sCompile isn't the bottleneck — waiting is
Teammates spamming pushesMultiple runs on one PR (see Fig. 2)Agent / bot loop
Expired certsSigning failures scattered across runsCold keychain, parallel contention

Fig. 2: one PR triggered six workflow runs

Queue pain was only act one. In March the team wired up a coding agent: CI red → read logs → auto-commit → trigger Actions again. PR #847's Checks page tells the story — six workflow runs, four actors bots, two human. Still called CI; behaviorally a retry loop. See what happens when CI gets looped.

Six GitHub Actions workflow runs on Pull Request #847
Fig. 2 · PR #847 · 6× ios-pr.yml triggers (4× bot · 2× human) · 2026-05-29

Someone on the team joked later: "CI didn't die — we turned it into a perpetual motion machine." The automation wasn't wrong; every bot fix was competing for the public queue with no dedicated runner pool.

Turning point: one Cloud Mac, and the pipeline split on May 31

After the spreadsheet, they broke logs down by job type: roughly 70% PR validation and integration builds, 20% Simulator tests, 10% Archive and upload — strikingly close to another team's measured mix at 500 iOS CI runs a day. Proposals flew: buy eight Mac minis, migrate to Xcode Cloud. Two things actually shipped:

May 31: rent one Apple Silicon Cloud Mac, register a self-hosted runner (labels self-hosted, macOS, cloud-mac). The A/B test was plain: same commit, fifty builds on hosted macos-latest vs. Cloud Mac — queued variance collapsed from "sometimes 40 minutes, sometimes 5" to "mostly single-digit seconds."

That night, split the pipelines. Move Archive, export, and upload out of ios-pr.yml into ios-release.yml; PR lane keeps build plus unit tests only. Warm-up cron too: nightly xcodebuild build on the Cloud Mac, DerivedData on disk — steadier than cold-pulling from actions/cache every run.

Fig. 6 · Same team — median wall time (PR build, 50 runs per two-week window)

Hosted macOS · queued
38 min
Hosted macOS · run
12 min
Cloud Mac self-hosted · queued
41 s
Cloud Mac self-hosted · run
9 min

Source: customer A/B logs (2026-05-31 — 06-08); cross-checkable with single runs in Figs. 1 and 4.

June 9: Fig. 4 — a Job page that finally looks sane

Two weeks after the split, Monday morning Slack: someone posted Run #18510488201 — queued 41s, run 9m 17s, runner self-hosted, cloud-mac. No applause in the channel; one emoji reaction. On an iOS team, that's high praise.

GitHub Actions self-hosted runner: Queued 41s, Run 9m17s, cloud-mac
Fig. 4 · Run #18510488201 · self-hosted, cloud-mac · 2026-06-09 09:11 CST

Fence in the agent: four rules on the Wiki

Splitting pipelines fixed Archive blocking PRs; the bot loop needed its own fix. The team gave bots a separate branch and a low-priority runner pool — humans review before merge. Four hard rules went on the Wiki:

  • PR workflow must not run Archive / store upload
  • macOS jobs must set concurrency — no stacked runs on one branch
  • Self-hosted runners must be label-scoped; no mixing with experimental workflows
  • Agent auto-push goes to a dedicated branch — never direct to protected branches
A 30-minute self-check you can copy
Open your last 10 macOS jobs; copy queue_wait_seconds and run duration (like Fig. 3). Count workflow runs per PR (like Fig. 2). Confirm Archive isn't still inside the PR workflow. If median queued > run, fix runner supply before tuning compile flags.

The new playbook: don't ditch GitHub — change the supply

This team's story boils down to four moves iOS shops actually shipped in 2026: Cloud Mac self-hosted runner pool, PR / Release split, warm-up and disk cache, agent guardrails. Can Xcode Cloud replace it? Great for Apple-native loops; teams mixing GitHub PR checks and Android lines mostly keep Actions and move Archive to dedicated macOS capacity.

If your team looks like…Typical landing pattern
1–3 people, weekly releasesHosted macOS + strong cache; manual Archive on release
5–15 people, daily merges1–2 Cloud Mac self-hosted + PR/Release split
15+ people, multiple appsRunner pool per app via labels + warm-up cron
Heavy agent-driven fixesSeparate bot pool + no Archive in PR workflow

Apple's xcodebuild docs stress scheme and destination consistency; if CI cold-starts every run, "passes locally" stops meaning anything. A customer put it roughly this way: staring at a 47-minute yellow bar every day hurts more than cert rotation or a major Xcode bump.

Start with one Cloud Mac self-hosted runner

If your Job page looks like Fig. 1 — yellow swallowing green — the best next dollar is usually one dedicated Apple Silicon Cloud Mac: register it as a GitHub self-hosted runner and turn PR builds from "queue lottery" into the predictable nine minutes in Fig. 4.

Run a two-week A/B: same branch, fifty builds on hosted macOS vs. Cloud Mac self-hosted, copy a sheet like Fig. 3. The data will tell you whether Xcode is slow or supply is slowing iOS development.

Start with one predictable Cloud Mac and put Archive back on the Release lane. See VPSSpark Cloud Mac plans and register your first iOS self-hosted runner.

Limited offer

iOS CI stuck in queue? Put a Cloud Mac on self-hosted runners

GitHub Actions · dedicated Apple Silicon · PR/Release split

Back to home
Limited offer See plans now