Three GitHub Actions runner funerals in five months. BuildJet announced its shutdown on February 6, 2026 and stopped running jobs on March 31. Cirrus Labs announced on April 7 that Cirrus CI would shut down June 1 as the team joined OpenAI, and closed Cirrus Runners to new customers. And in between, one repo's public migration log shows a WarpBuild-to-Blacksmith macOS move merged, reverted over 65-minute queue times, then re-applied once capacity recovered.
Three different flavors of the same risk. The runs-on label you changed in one line answers to someone else's balance sheet and capacity planning, and when their answer changes, the bill lands on your sprint. This post itemizes that bill — what an offboarding actually costs a team with no fallback pool — and describes the fallback that turns "our CI vendor died" from a fire drill into a label change.
The offboarding bill
Every vendor exit charges the same five line items. Only the amounts vary:
| # | Line item | What it costs mid-sprint | Seen in |
|---|---|---|---|
| 1 | Re-pointing every workflow label | Every runs-on with the vendor prefix must move, across every workflow file, including cron, release, and docs jobs you forgot existed | BuildJet: buildjet-* to ubuntu-latest; cmux: three label flips in weeks |
| 2 | Replacing the vendor cache backend | Proprietary cache actions get swapped for actions/cache, and every cache key starts cold on the new fleet | BuildJet's own guide: buildjet/cache to actions/cache |
| 3 | Replacing the static-IP / allowlist story | If you allowlisted the vendor's egress IPs for staging databases or registries, the new fleet's IPs must be allowlisted and verified before main goes green again | Any team behind an IP allowlist |
| 4 | Re-qualifying build times and concurrency | New hardware means new timings: different CPUs, different queue depths, sometimes missing preinstalled tools (one team had to add a Docker install step when moving ARM jobs to GitHub-hosted runners) | cmux: 65-minute queues to 16-second pickup across one revert cycle |
| 5 | Re-tuning minutes budgets and quotas | Per-minute prices, free tiers, and concurrency caps all reset; macOS minutes in particular can cost an order of magnitude more than the Linux numbers your budget assumed | Blacksmith macOS M4 pricing vs. Linux per-minute rates |
None of these is hard in isolation. Together, under a shutdown deadline, they are a multi-day tax on a team that planned to ship features that week. The rest of this post walks the three cases, then the fallback.
Case 1: BuildJet — the clean shutdown that still touched everything
BuildJet's shutdown was, as these things go, a model exit. The announcement gave almost eight weeks of notice: new signups halted February 6, concurrency subscriptions made free immediately, pay-as-you-go billing continuing normally until jobs stopped March 31. The migration guide was two diffs: swap runs-on: buildjet-2vcpu-ubuntu-2204 for ubuntu-latest (or ubuntu-24.04-arm for ARM workflows), and swap buildjet/cache@v4 for actions/cache@v4.
The stated reason is worth reading closely. BuildJet said GitHub had closed the gap it was founded to fill: faster hardware, larger runner options, and native ARM runners. That is both gracious and instructive — the vendor's moat was GitHub's roadmap, and roadmaps move. When your runner vendor's advantage is "faster than stock," stock catching up is an existential event you will read about in a shutdown post.
Even this cleanest-case exit still billed all five line items. Public migration commits show teams re-pointing labels across multiple workflow files, re-basing on new runner images, and discovering environment differences the hard way — one ARM migration had to add an explicit Docker install step because the new runners did not preinstall it. Multiply that by every workflow in the repo and you get the real price of a "one-line change."
Case 2: Cirrus — the acqui-hire shutdown
Cirrus Labs announced on April 7, 2026 that Cirrus CI would stop running jobs on June 1, as the team joined OpenAI's Agent Infrastructure group. Cirrus Runners, the GitHub Actions runner product, stopped accepting new customers while continuing to support existing ones through their contract periods. This is the second flavor of exit: not a wind-down, an acqui-hire, where the product's future is decided by someone else's hiring plan.
The downstream scramble is visible in public. The conda-forge project removed Cirrus Runners support from its CI registration tooling and added Blacksmith and Namespace instead. The SciPy maintainers opened a "CI: Cirrus Runners will disappear soon" issue to track replacements. Note what that means: the cost of a shutdown is not just your own workflows, it is the ecosystem tooling around you ripping out support on its own schedule, which narrows your options the longer you wait.
The acqui-hire flavor also shortens the fuse on line items 3 and 4 of the bill. Contract-period support means your existing capacity is frozen, not growing — good luck scaling up CI for a release while your vendor is onboarding at its acquirer. Teams that had no second fleet found themselves negotiating a migration and a hiring-season release freeze at the same time.
Case 3: the merge, the revert, and the re-apply — capacity as a slow-motion shutdown
The third flavor never announces. It just gets slow. The manaflow-ai/cmux repository's public CI history reads like a stress test of vendor dependence: a WarpBuild/Depot-to-Blacksmith macOS migration merged, then reverted because effectively-zero Blacksmith macOS capacity produced 65-minute-plus queues and cancelled main-branch runs, then re-applied once all three Blacksmith macOS SKUs were picking up jobs in about 16 seconds.
Read that sequence again, because it contains the whole argument. The team did everything right — they migrated deliberately, they measured, they reverted on data, they re-applied when capacity recovered. And they still paid the offboarding bill three times in a matter of weeks: three rounds of label flips, three rounds of re-qualified timings, plus cancelled runs on main. Capacity degradation is a shutdown in slow motion, except there is no announcement post and no deadline — just a queue graph that keeps getting worse until someone reverts.
The epilogue sharpens the point. Months later, the same repo moved its macOS-15 jobs again, this time off a retired blacksmith-6vcpu-macos-15 pool label. Vendor pool labels retire. SKUs get renamed. The runs-on string that worked last quarter can stop resolving for reasons that have nothing to do with your code, and the fix is yet another label change under time pressure.
The fallback: indirection plus a floor
Two mechanisms, both cheap to build before you need them, convert every flavor above into a routine change.
First, indirect your labels. The cmux repo's lasting fix was not picking the right vendor — it was routing every paid macOS job through MACOS_RUNNER_15 / MACOS_RUNNER_26 repository variables with a Warp literal fallback, so the provider flips with a variable change and no pull request. Copy that pattern for every vendor label in your workflows: no raw vendor string in runs-on, only a variable that resolves to one. The next migration — shutdown, capacity crunch, or retired SKU — becomes a settings-page edit instead of a multi-file diff, and it can be done by whoever is on call, not whoever owns the CI files.
Second, stand up a floor. Keep a self-hosted pool behind one of those variables at all times, even if it normally takes zero jobs. The standard shape in 2026 is Actions Runner Controller (ARC), GitHub's Kubernetes operator for autoscaling runner scale sets: ephemeral pods per job, scaling from zero to whatever the queue demands, installed with Helm into your own cluster. A minimum ARC pool on nodes you already own costs almost nothing at idle and answers the "where do jobs go right now" question the minute a vendor fails.
Price the floor honestly. Since March 1, 2026, GitHub charges $0.002 per minute for Actions orchestration on self-hosted runners in private repositories (public repos remain free). That is still far below any vendor's per-minute rate, but it is not zero, and your minutes budget should assume the fallback pool at full sprint load — because the week you need it, it will be.
With indirection plus a floor, the three cases above play out differently. BuildJet's shutdown becomes a variable flip to your ARC pool on day one, then a calm evaluation of the next vendor. Cirrus's freeze becomes irrelevant to your release schedule. And the cmux capacity saga becomes a same-day flip to the vendor with capacity, or to your own pool, without a revert PR and without cancelled main-branch runs.
Do this before your vendor's announcement
The pattern across all three cases is that the teams with a fallback treated each exit as a chore, and the teams without one treated it as an incident. Four steps put you in the first group:
- Audit your labels. List every
runs-onvalue across every workflow, including cron and release jobs. If a vendor string appears anywhere, it is load-bearing. - Abstract them behind variables. One repo or org variable per runner family, with a documented default. Verify the flip works by pointing one family at stock GitHub runners for a day.
- Stand up a minimum ARC pool. Autoscaled to zero on your own cluster, behind one of those variables, running at least one canary job so you know it is alive.
- Write down the bill. For each vendor, note its cache backend, its egress IPs, and its minutes pricing, in the same doc as the variables. The next offboarding starts from that page instead of from archaeology.
Your CI vendor is a dependency with a balance sheet. Treat it like one: pin it behind an interface, keep a second implementation warm, and never let a runs-on string be the only thing standing between your team and a green main branch.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Your fallback runner pool is just another workload that deserves owned infrastructure. Star the repo on GitHub or deploy your first app today.



