The short version
- In December 2025 GitHub announced a per-minute charge for self-hosted runners, then postponed it within days after developers pushed back.
- Luminair's release is one dispatch-only workflow: it tags a clean checkout, builds each platform into a draft nobody can see, checks every file's checksum, then publishes all at once.
- On 6 September 2026 both Mac lanes and the per-push test gate moved onto a self-hosted Mac. Signing there took two days of keychain fixes.
- “Zero minutes” is true for the expensive parts, not every job. And building on a desk has a cost of its own, which made us put one path back on GitHub's Macs.
Paying rent on your own machine.
On 16 December 2025 GitHub announced two changes to Actions pricing. Hosted runners would get cheaper, “by up to 39%”, on 1 January 2026. And on 1 March 2026, “a new $0.002 per minute GitHub Actions cloud platform charge that will apply to self-hosted runner usage.” The post's own FAQ asked the obvious question: “Why am I being charged to use my own hardware?”
The response was fast. The Hacker News thread ran to hundreds of comments. By the next day, as The Register reported, GitHub had walked it back. The changelog now opens with the update: “We're postponing the announced billing change for self-hosted GitHub Actions to take time to re-evaluate our approach.” It adds that “we missed the mark with this change by not including more of you in our planning.” The Register noted the obvious caveat: GitHub “didn't say it won't ever go forward with charging for self-hosted runners, only that it's postponing the change.”
For a small team shipping a Mac app, the interesting part is not the fee. It is the reminder that a release pipeline sits on someone else's pricing decisions. Mac runners are the expensive ones; our own workflow's warning calls them “10x minutes”.
A draft first, then everything at once.
Before minutes, there was a worse problem. The comment at the top of the release workflow says it was “Designed 2026-09-03 after a day lost to partial releases.” Until then each platform had its own workflow, and a release could go out with some of its files missing.
So since 3 September 2026 one workflow owns the whole release, and it runs only when dispatched. Its design rules are listed in its header, and they read like a checklist:
Fail in a minute, not at publish
The first job checks every credential the whole release needs before any 30-minute build starts. It then bumps the version and tags from a fresh checkout, so a laptop's half-finished edits can never leak into a release. Only one release may be in flight at a time.
Native lanes, three attempts each
Apple Silicon and Intel build as separate lanes. Each re-runs the full test gate, then builds, signs and notarizes the app and its disk image, retrying from a clean state up to three times, and uploads into a draft release with a sha256 manifest.
Every byte, before anyone sees it
Finalize checks that every file named in every manifest is on the draft with a size above zero, downloads each file the site and the updater serve, and compares its sha256 with what the lane built.
One flip, then a real download test
Publish rechecks the draft, flips it to latest, and then downloads the first byte of every site button and both update feeds from the public URL. A dead button fails the run.
A single-architecture release is possible too; the other chip's files are carried forward from the current release so no button on the site breaks. Windows is opt-in and carried forward the same way.
The runner is the Mac already on.
On the morning of 6 September 2026 two commits landed a few minutes apart. The first made the release route both Mac lanes to a self-hosted Mac runner when it is online, Apple Silicon natively and Intel through Rosetta, with GitHub's hosted Macs only as a fallback. The second moved the per-push test gate onto the same Mac and dropped the hosted Linux and Mac gate jobs. The CI file's comment says it is the same macOS gate the release lanes run, at zero GitHub minutes, and is blunt about the catch: “If that Mac is offline the run simply waits”.
The workflow cannot see which runners are online by itself; the default token is not allowed to list them. So the dispatcher decides. A small menu-bar tool we use internally lists the Macs that are online and fills in which lane goes where: two Macs online build the two lanes in parallel, and an Intel Mac takes the Intel lane natively. The workflow validates those labels and falls back to safe defaults if anything looks wrong.
Signing on a Mac somebody uses.
Signing a Mac app in CI means putting a Developer ID certificate into a keychain the signing tool can reach. On a throwaway hosted machine that is simple. On a Mac where a person is also working, every shortcut shows up as a password dialog on their screen. Here is what the history records, in order:
Clean up, always
A self-hosted Mac is not thrown away after the job. The Mac lane now always restores the original keychain search list and deletes its temporary keychain, even when the build fails.
The keychain locked itself mid-build
The Apple Silicon lane failed at the disk image step with errSecInternalComponent, after the Intel lane had passed the same step. A fresh keychain auto-locks after 300 seconds, and about six minutes in, macOS popped a dialog asking for a random password nobody knew. The fix: a unique keychain per job, a 12-hour timeout, and an unlock before every signing-heavy step.
Out of the search list: fails
A hardening change moved the temporary keychain out of the user's search list and pointed the signer at it directly. Version 1.0.102 then failed three times on the same file. The signing tool finds the identity and its certificate chain by walking the search list.
First in line: works
The fix put the temporary keychain back first in the search list, the layout that had signed the two previous successful releases, and made cleanup restore the original list.
The dialog on the desk
One problem remained. GitHub's service template gives the runner its own security session, so the job's unlock never reached the person's session. Any other signing on that Mac hit the locked keychain first and raised the password dialog, which the commit says “has appeared on every release since the temp keychain was introduced.” Our tool now rewrites the runner service to share the logged-in session.
Out of the list
The signer cannot build the certificate chain.Even with the keychain named explicitly.
Last in the list
The same identity is found in the login keychain first.Wrong copy, same error.
First in the list
Restored after every job, success or not.Needs the runner in the logged-in session.
Free minutes, busy desk.
Here is the part the title leaves out. Building the Mac lanes on a working Mac means running the full test suite, the app build and notarization there. Luminair binaries get launched over and over and the Dock icon keeps bouncing, which, as the source puts it, “reads exactly like the app relaunching itself.”
On 23 September 2026 that was reason enough to change the default of our command-line release script. Building on the local Mac is now opt-in there, with an environment switch; without it, the Mac lanes go to GitHub's hosted runners and nothing builds on the desk. The menu-bar tool still routes to online self-hosted Macs, and the per-push gate still runs on the self-hosted Mac.
Zero for the expensive parts.
To be precise about the headline:
- Zero hosted minutes: both Mac build lanes when they run on a self-hosted Mac, and the per-push test gate.
- Still hosted: the cut, finalize and publish jobs on Linux runners, the Windows lane when it is requested, and the Mac lanes whenever no self-hosted Mac is used.
That split was the point. The Mac lanes are the long, expensive jobs; the bookkeeping jobs are short, and keeping them on clean hosted machines means the step that makes a release public never depends on the state of someone's desk.
Checked, and not claimed.
What this post does not claim
- How much money this saved. We did not add up our Actions bill before and after.
- That every job costs zero minutes. The bookkeeping jobs and the optional Windows lane run on hosted runners.
- That the keychain layout is right for every Mac. It is the one that signs on ours, recorded with the runs that proved it.
- What GitHub will charge for self-hosted runners in future. It postponed the change; it did not rule it out.
Sources
- GitHub Changelog · 16 December 2025, updatedUpdate to GitHub Actions pricing
- The Register · 17 December 2025GitHub walks back plan to charge for self-hosted runners
- Hacker News · December 2025Pricing Changes for GitHub Actions
Get the build this pipeline made
Signed, notarized, for Apple Silicon and Intel.