What macOS Runners Cost on GitHub Actions
GitHub-hosted macOS runners list at $0.062 to $0.102 per minute. WarpBuild macOS runners are $0.08 per minute for 6 vCPU and $0.16 for 12 vCPU.
Last verified:
GitHub publishes per-minute rates for its hosted macOS runners of $0.062 for the 3-core Apple Silicon image called macos-latest, $0.077 for the 12-core Intel image called macos-latest-large, and $0.102 for the 5-core Apple Silicon image called macos-latest-xlarge (GitHub Actions per-minute rates, checked on 2026-08-13). WarpBuild macOS runners are $0.08 per minute for 6 vCPU with 22GB of memory and $0.16 per minute for 12 vCPU with 44GB of memory. Pricing is purely usage based. There is no base subscription fee, no platform fee, and no seat fee.
Diagnosis
macOS is the most expensive operating system on GitHub Actions, and an iOS pipeline spends almost all of its wall clock there. Four things stack up.
The per-minute rate leads every other runner OS. GitHub's published rate for the 5-core Apple Silicon macos-latest-xlarge image is $0.102 per minute, against $0.006 per minute for a 2-core hosted Linux runner (GitHub Actions per-minute rates, checked on 2026-08-13). That is a $0.096 gap per minute inside GitHub's own list, and an iOS pipeline spends its minutes on the expensive side of it. Minute count is the lever that moves the bill.
Included minutes evaporate. GitHub applies a minute multiplier to macOS when a job draws on the allowance that comes with a plan, so one macOS minute consumes ten minutes of that allowance (GitHub Actions billing documentation, checked on 2026-08-13). Teams read their first two invoices as an anomaly. The allowance was gone by the fourth working day.
Archive and export jobs are long and serial. A release job that resolves Swift Package Manager dependencies, compiles Release configuration from a cold DerivedData directory, signs, archives, and exports an IPA runs for tens of minutes with no useful parallelism inside it. Compilation is the bulk, and every cache miss on DerivedData or on the SPM checkout directory turns a warm 6-minute compile into a cold one.
Simulator boot time is billed time. xcrun simctl boot on a cold runner takes a real fraction of a minute before the first test executes, and a first-run device may also install a runtime. A test job that boots three destinations pays that cost three times. On a 500-run month, one wasted minute per run is 500 macOS minutes on the invoice.
Device matrices serialize when concurrency is capped. A strategy.matrix with four simulator destinations is only cheaper in wall clock when four macOS runners start at once. When macOS capacity is limited, the matrix legs queue, the pull request feedback loop stretches to half an hour, and engineers stop waiting for green before they merge. The billed minutes stay the same, so the team pays full price for a slow signal.
Two more items show up in reviews of real iOS repositories. Jobs that do not need Xcode at all, such as SwiftLint runs, changelog validation, license checks, and App Store metadata linting, sit inside the macOS job because that is where the checkout already exists. And xcodebuild runs with default settings write result bundles and build logs that later steps upload one file at a time, which adds minutes at the macOS rate.
Fix
Move macOS jobs onto WarpBuild macOS labels, then cut the macOS minute count itself. The first step is a one-line change per job, and the second is ordinary workflow surgery.
Change the label. WarpBuild runners register with GitHub as self-hosted runners carrying warp- labels, so replacing the value of runs-on is the entire migration for a job. WarpBuild provides Linux x64, Linux ARM64, macOS, and Windows runners, and the macOS images carry the same tooling as the GitHub-hosted macOS images, which is what makes them drop-in. The macOS catalog offers multiple sizes and configurations per chip, so a job picks 6 vCPU or 12 vCPU by label.
Match the size to the job. A pull request test job on a simulator rarely saturates 12 vCPU, so warp-macos-15-arm64-6x at $0.08 per minute is the right default. An archive job that compiles Release configuration from cold does use the wider machine, and warp-macos-26-arm64-12x at $0.16 per minute pays for itself only if it finishes in fewer minutes, so measure the job both ways before you settle. Storage differs too: 120GB of SSD on the 6 vCPU size and 270GB on the 12 vCPU size, which matters once DerivedData, multiple simulator runtimes, and an archive share a disk.
Get the non-Xcode work off macOS. Lint, metadata validation, JSON and YAML checks, and changelog rules run on warp-ubuntu-latest-arm64-4x at $0.006 per minute. The arithmetic in the cost model below shows what that single split is worth on a 500-run month.
Stop paying for repeated simulator boots. Boot one simulator in a dedicated step, keep the destination stable across the test invocations in the job, and pass -derivedDataPath explicitly so a cache action can restore it.
Fan the matrix out instead of letting it queue. Run as many jobs as your workflows need. Generally available Linux and Windows runners do not have plan-level concurrency caps. For unusually high macOS concurrency, write to [email protected] before the first heavy release week.
Keep image coverage in mind when pinning Xcode. WarpBuild ships five macOS labels across macOS 14, macOS 15, and macOS 26, and the macOS 26 images add Xcode 27.0 (build 27A5194q) on top of the upstream GitHub image, with iOS, tvOS, watchOS, and visionOS 27.0 simulator runtimes. A team that needs an older toolchain for a maintenance branch keeps that branch on warp-macos-14-arm64-6x at the same $0.08 per minute.
The outcome carries a number: warp-macos-latest-arm64-6x (6 vCPU, 22GB) costs $0.08 per minute against $0.102 per minute for the largest GitHub-hosted macOS ARM64 runner (5 vCPU, 14GB), which is 22 percent lower list price. GitHub list price checked on 2026-08-13, and the arithmetic below carries it through a monthly volume.
Configuration
Two workflow files cover the common iOS shape. The first is the pull request workflow, with the non-Xcode job on Linux and the test job on a 6 vCPU macOS runner.
name: ios-pull-request
on:
pull_request:
branches: [main]
concurrency:
group: ios-pr-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: warp-ubuntu-latest-arm64-4x
steps:
- uses: actions/checkout@v4
- run: ./scripts/lint-swift.sh
- run: ./scripts/validate-app-store-metadata.sh
build-and-test:
runs-on: warp-macos-15-arm64-6x
steps:
- uses: actions/checkout@v4
- name: Restore SPM and DerivedData
uses: actions/cache@v4
with:
path: |
~/Library/Developer/Xcode/DerivedData
~/Library/Caches/org.swift.swiftpm
key: xcode-${{ hashFiles('**/Package.resolved') }}
restore-keys: xcode-
- name: Boot one simulator for the whole job
run: |
xcrun simctl boot "iPhone 16" || true
xcrun simctl bootstatus "iPhone 16" -b
- name: Test
run: |
xcodebuild test \
-scheme AppTests \
-destination "platform=iOS Simulator,name=iPhone 16" \
-derivedDataPath ~/Library/Developer/Xcode/DerivedData \
-resultBundlePath TestResults.xcresult \
CODE_SIGNING_ALLOWED=NO
- name: Upload result bundle
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: TestResults.xcresultThe second file is the nightly and release path on the 12 vCPU macOS runner, which is where the newest image and the wider machine earn their rate.
name: ios-archive
on:
schedule:
- cron: "0 6 * * 1-5"
workflow_dispatch:
jobs:
archive:
runs-on: warp-macos-26-arm64-12x
steps:
- uses: actions/checkout@v4
- name: Select Xcode 27.0
run: sudo xcode-select -s "$(ls -d /Applications/Xcode_27*.app | tail -1)"
- uses: apple-actions/import-codesign-certs@v3
with:
p12-file-base64: ${{ secrets.SIGNING_CERT_P12 }}
p12-password: ${{ secrets.SIGNING_CERT_PASSWORD }}
- name: Archive
run: |
xcodebuild archive \
-scheme App \
-configuration Release \
-destination "generic/platform=iOS" \
-archivePath build/App.xcarchive
- name: Export IPA
run: |
xcodebuild -exportArchive \
-archivePath build/App.xcarchive \
-exportOptionsPlist ExportOptions.plist \
-exportPath build/export
- uses: actions/upload-artifact@v4
with:
name: ipa
path: build/export/*.ipaA device matrix belongs in its own job so the legs start in parallel instead of queueing behind one another:
ui-tests:
runs-on: warp-macos-15-arm64-6x
strategy:
fail-fast: false
matrix:
destination:
- "platform=iOS Simulator,name=iPhone 16"
- "platform=iOS Simulator,name=iPhone 16 Pro Max"
- "platform=iOS Simulator,name=iPad (10th generation)"
steps:
- uses: actions/checkout@v4
- run: |
xcodebuild test \
-scheme AppUITests \
-destination "${{ matrix.destination }}" \
CODE_SIGNING_ALLOWED=NOFour configuration facts decide whether the move is clean:
- Aliases exist for the latest images.
warp-macos-latest-arm64-6xresolves towarp-macos-15-arm64-6xandwarp-macos-latest-arm64-12xresolves towarp-macos-15-arm64-12x, in step with GitHub's ownmacos-latestpointer. Pinning the explicit version label keeps a release branch stable when the pointer moves. - macOS runners have no Docker and no nested virtualization. Container builds and Android emulator jobs belong on Linux x64 runners, where the
nested-virtualization.enabled=truelabel grants/dev/kvmaccess. - The WarpBuild cache is a Linux runner feature. On macOS, use
actions/cachefor DerivedData and the SPM cache directory, as shown above, and keep the cache key tied toPackage.resolved. - Runner storage is ephemeral. Every job starts on a fresh VM, so anything the next job needs goes to an artifact or a cache.
The label list, the sizes, and the per-minute rates live in the cloud runners documentation, which also carries the macOS 26 tooling table with the bundled simulator runtimes.
Cost or Time Model
The two price tables
WarpBuild macOS runners, from the runner catalog:
| Label | macOS | vCPU | Memory | Storage | Rate per minute | Alias |
|---|---|---|---|---|---|---|
warp-macos-26-arm64-6x | macOS 26 | 6 | 22GB | 120GB SSD | $0.08 | |
warp-macos-26-arm64-12x | macOS 26 | 12 | 44GB | 270GB SSD | $0.16 | |
warp-macos-15-arm64-6x | macOS 15 | 6 | 22GB | 120GB SSD | $0.08 | warp-macos-latest-arm64-6x |
warp-macos-15-arm64-12x | macOS 15 | 12 | 44GB | 270GB SSD | $0.16 | warp-macos-latest-arm64-12x |
warp-macos-14-arm64-6x | macOS 14 | 6 | 22GB | 120GB SSD | $0.08 |
GitHub-hosted macOS runners, at GitHub's published list rates:
| GitHub-hosted label | Chip | vCPU | Memory | Rate per minute |
|---|---|---|---|---|
macos-latest (actions_macos) | Apple Silicon | 3 | 7GB | $0.062 |
macos-latest-large (macos_l) | Intel | 12 | 30GB | $0.077 |
macos-latest-xlarge (macos_xl) | Apple Silicon | 5 | 14GB | $0.102 |
Rates read from GitHub Actions per-minute rates, checked on 2026-08-13. GitHub bills these rates on paid plans once included minutes are consumed. The matched shape for a WarpBuild comparison is macos-latest-xlarge, GitHub's Apple Silicon macOS runner, since macos-latest-large is Intel hardware and macos-latest is a smaller 3 vCPU machine. WarpBuild rates come from the pricing page and the cloud runners documentation, verified the same day.
The arithmetic on one minute
The like-for-like comparison is against macos-latest-xlarge, because Apple Silicon on Apple Silicon is the closest published shape between the two catalogs.
- GitHub-hosted
macos-latest-xlarge: 5 vCPU, 14GB of memory, $0.102 per minute. - WarpBuild
warp-macos-15-arm64-6x: 6 vCPU, 22GB of memory, $0.08 per minute, one more vCPU and 8GB more memory on top. - $0.102 minus $0.08 is $0.022 per minute, which is 22 percent lower list price at the WarpBuild rate.
- 100 macOS minutes cost $10.20 at GitHub's list rate and $8.00 at the WarpBuild rate.
- 1,000 macOS minutes cost $102.00 and $80.00.
GitHub's other two macOS shapes sit outside this comparison. The 3 vCPU macos-latest runner is $0.062 per minute, the cheapest macOS minute on either price list, and it draws on a plan's included minutes the way macos-latest-xlarge does not. The 12 vCPU macos-latest-large runner is Intel hardware at $0.077 per minute, a different chip family from WarpBuild's Apple Silicon fleet, so it is not a matched size either. The Apple Silicon pair above is the comparison this page carries through the model below.
Worked monthly model
Assumptions, all stated so you can substitute your own numbers:
- One iOS repository, 8 engineers, 20 working days in the month.
- 25 pull request runs per working day, so 500 pull request runs in the month.
- Each pull request run is one macOS job of 14 minutes: 4 minutes of lint, metadata, and changelog checks plus 10 minutes of build and simulator tests.
- One nightly archive job per working day at 22 minutes, so 20 runs.
- One release job per week at 35 minutes, so 4 runs.
- Every job runs to completion. Cancelled runs and reruns are excluded.
- Billing is per minute of job time on both platforms.
- Artifact storage, cache storage, and cache operations are excluded from both columns so the runner minute is the only variable.
Minute totals: 500 x 14 = 7,000, plus 20 x 22 = 440, plus 4 x 35 = 140. That is 7,580 macOS minutes per month.
| Configuration | macOS minutes | Linux minutes | Monthly runner cost |
|---|---|---|---|
Everything on GitHub-hosted macos-latest-xlarge | 7,580 | 0 | $773.16 |
Everything on warp-macos-15-arm64-6x | 7,580 | 0 | $606.40 |
| Split, GitHub-hosted macOS plus hosted Linux 2-core | 5,580 | 2,000 | $581.16 |
Split, warp-macos-15-arm64-6x plus warp-ubuntu-latest-arm64-4x | 5,580 | 2,000 | $458.40 |
The arithmetic behind each row:
- 7,580 x $0.102 = $773.16.
- 7,580 x $0.08 = $606.40. Difference against the first row: $166.76 per month, or $2,001.12 over twelve months at the same volume.
- Moving the 4 minutes of non-Xcode work per pull request run off macOS removes 500 x 4 = 2,000 minutes from the macOS column. GitHub-hosted: 5,580 x $0.102 = $569.16, plus 2,000 x $0.006 = $12.00, total $581.16.
- WarpBuild: 5,580 x $0.08 = $446.40, plus 2,000 x $0.006 = $12.00, total $458.40. The split alone saves $148.00 per month against the all-macOS WarpBuild row, and combining the switch with the split is what separates the cheapest row from the rest.
Signup includes $10 free credits, which covers 125 minutes on a 6 vCPU macOS runner at $0.08 per minute, or roughly 12 pull request test jobs from the model above before the first invoice.
Where the model breaks
Three assumptions in that table are worth testing against your own repository before you trust the total.
Cold caches. The 10-minute test job assumes DerivedData restores. A month of cache misses turns 10 minutes into 16 and adds 500 x 6 x $0.08 = $240.00 at the WarpBuild rate, or 500 x 6 x $0.102 = $306.00 at GitHub's list rate. Cache keys deserve as much review as the runner label.
Reruns. Flaky UI tests that get rerun twice a day add 40 macOS jobs a month. At 14 minutes each that is 560 minutes, $44.80 at $0.08 per minute.
Matrix width. Adding a fourth simulator destination to the UI test job adds a full job per run. Cheap in wall clock when the legs run in parallel, and linear on the invoice either way.
Read the real numbers instead of the model once you are running. The reports page breaks billing down per job, showing repository, job name, runner label, execution time, billed time, and cost, with CSV export and filters by repository and runner label. CI observability adds per-job CPU and memory percentiles, which is how you tell a 12 vCPU job that needed the width from one that idled at 6.
For a full list of runner sizes and rates across every platform, see the WarpBuild pricing page. For macOS specifics, start with WarpBuild macOS runners for GitHub Actions and the Xcode versions on WarpBuild macOS images. For the pipeline shape around the runner, see iOS pipelines on GitHub Actions, and for the same exercise across Linux and Windows jobs, see reduce GitHub Actions costs.
FAQ
How much do GitHub-hosted macOS runners cost per minute?
GitHub publishes $0.062 per minute for the 3-core Apple Silicon image (macos-latest), $0.077 per minute for the 12-core Intel image (macos-latest-large), and $0.102 per minute for the 5-core Apple Silicon image (macos-latest-xlarge). Rates read from the GitHub Actions per-minute rates documentation, checked on 2026-08-13. Confirm on GitHub's page before you budget, because GitHub revises the table.
What does a WarpBuild macOS runner cost?
$0.08 per minute for 6 vCPU with 22GB of memory and 120GB of SSD, and $0.16 per minute for 12 vCPU with 44GB of memory and 270GB of SSD. Billing is per minute of job time. Pricing is purely usage based. There is no base subscription fee, no platform fee, and no seat fee.
Which WarpBuild label replaces macos-latest in an iOS workflow?
warp-macos-15-arm64-6x, which also answers to the alias warp-macos-latest-arm64-6x. For a larger machine on the newest image, use warp-macos-26-arm64-12x, which carries 12 vCPU, 44GB of memory, and Xcode 27.0.
Can macOS runners run Docker or an Android emulator?
No. WarpBuild macOS runners do not support nested virtualization and cannot run Docker. Send container builds and Android emulator jobs to Linux x64 runners, where the nested-virtualization.enabled=true label grants /dev/kvm access.
Start with $10 in free credits
Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.