Why your GitHub Actions bill is mostly macOS
A single macOS job can cost more than twenty Linux jobs doing the same work. If your CI bill surprises you, the runner label is usually the reason.
The arithmetic
| Runner | Per minute | 1,000 minutes |
|---|---|---|
ubuntu-latest | $0.006 | $6.00 |
windows-latest | $0.010 | $10.00 |
macos-latest | $0.062 | $62.00 |
macOS is about 10× Linux and Windows about 1.7×. On private repositories every job is billed; on public repositories standard Linux and Windows minutes are free, while macOS is metered at its higher rate. Check your own usage page for your account's specifics.
Billing rounds up per job to the next whole minute, so a 61-second macOS job bills two minutes.
Does the job actually need macOS?
That is the whole question, and it is not answered by the label. A job needs macOS only if something in it does:
- runs Xcode tooling —
xcodebuild,xcrun,xcode-select,actool,ibtool - uses Apple packaging or signing —
codesign,notarytool,stapler,create-dmg,productbuild - installs with Homebrew or CocoaPods —
brew install,pod install - builds Swift —
swift build,swift test, aPackage.swift - builds an Xcode project — an
.xcodeprojor.xcworkspace - is one leg of an intentional OS matrix, testing that the code works on macOS
If none of those apply — the job only runs Node or Python tests, lints, or builds a portable binary — the label is usually a leftover, and it is the most expensive leftover in CI.
The change
build: - runs-on: macos-latest + runs-on: ubuntu-latest
One line. If the toolchain is genuinely portable but slower or faster on Linux, the duration changes too, so re-measure afterwards rather than assuming.
How to tell whether it worked
Compare billed minutes per run before and after, not wall-clock or dollar totals: 10 comparable runs over at least 14 days on each side, with the same trigger and the same workflow. A price change would otherwise look like a saving, and an unusually slow week would look like a regression.
When not to do this
Do not convert a job in a release or deployment workflow, and do not convert the macOS leg of an OS matrix — that job exists to test macOS. CIPatch abstains from both by default, which is why its runner suggestions are rare and worth reading.
CIPatch checks every job for macOS and Windows runners that nothing in the job requires. The scan is read-only, needs no account for the first repository, and changes nothing.