# Cirrus CI shutting down: moving the macOS jobs first

> Cirrus CI shutting down on 1 June 2026 leaves every .cirrus.yml task homeless. The GitHub Actions migration, with macOS rates read 2026-09-20.

Source: https://latchkey.dev/learn/runners/migrate-from-cirrus-ci  
Updated: 2026-09-20

With Cirrus CI shutting down on 1 June 2026 and Cirrus Runners closed to new customers, every task in your `.cirrus.yml` needs a new home, and this is not the one-line label swap that a runner migration usually is. Cirrus is a platform rather than a runner, so the Linux half is a rewrite into a workflow file and the macOS half is a rewrite plus a bill you should price before you commit to it.

Two products are ending on two timetables, and mixing them up is the first mistake. Cirrus Labs announced that "Cirrus CI will shut down effective Monday, June 1, 2026", which is the hosted platform that reads your `.cirrus.yml`. Cirrus Runners, the separate product that attaches macOS and Linux runners to GitHub Actions, is a slower wind-down: the same announcement says the company is "no longer accepting new customers" and will "continue supporting the service for existing customers through their existing contract periods".

The documentation has gone ahead of the service. Checked on 2026-09-20, `cirrus-ci.org` no longer resolves and `cirrus-ci.com` does not answer, so the only authoritative description of what your pipeline does is the `.cirrus.yml` in your own repository. Export anything you need from the Cirrus dashboard now rather than later.

One piece of good news: Tart, Vetu and Orchard are being relicensed "under a more permissive license" with the licensing fees dropped, so the macOS virtualization stack underneath Cirrus survives even though the hosted service does not.

## The pre-flight checklist

A Cirrus migration is a translation, not a relabel, so the inventory matters more than usual. Work through all eight before you write a workflow file.

- **Instance types.** Every `macos_instance`, `container`, `windows_container` and `persistent_worker` in the file is a different destination. The macOS ones are the expensive ones and should be listed first.
- **Runner sizes.** Cirrus tasks declare CPU and memory on the instance. Write those numbers down: the GitHub-hosted default is 2 vCPU with 8 GB on private repositories, and a task that asked for more will be slower without saying so.
- **Secrets.** Cirrus encrypted variables and `CIRRUS_*` environment values have no equivalent. Every one needs a repository or environment secret on the GitHub side, created before the first run rather than after the first failure.
- **Cache keys.** A `*_cache` block carries `folder`, `fingerprint_script` and `populate_script`. The folder becomes `path`, the fingerprint becomes a `key` built with `hashFiles`, and the populate script becomes an ordinary step guarded by `cache-hit`.
- **Matrix.** Cirrus `matrix` expands inside a task. GitHub `strategy.matrix` expands into jobs, so the fan-out is the same but the concurrency ceiling is not: GitHub allows 20 to 500 concurrent jobs by plan and only 5 concurrent macOS jobs on Free, Pro and Team.
- **Artifacts.** An `artifacts` instruction becomes `actions/upload-artifact`, and anything that was reading artifacts from the Cirrus API needs a new source.
- **Concurrency.** Cirrus `auto_cancellation` becomes a `concurrency:` block with `cancel-in-progress: true`.
- **Anything that assumed a persistent worker.** A `persistent_worker` task ran on a machine you controlled, with state between runs. On ephemeral runners that state is gone every time.

## Before and after: a macOS task

The shape below is the one most repositories used: a `macos_instance` with a Tart image, then named script instructions. Translating it is mechanical once the secrets and the cache are in place.

```.cirrus.yml to .github/workflows/ci.yml
# before: .cirrus.yml
macos_instance:
  image: ghcr.io/cirruslabs/macos-runner:sonoma

test_task:
  deps_cache:
    folder: ~/.cache/pods
    fingerprint_script: cat Podfile.lock
    populate_script: pod install
  test_script: ./scripts/test.sh

# after: .github/workflows/ci.yml
jobs:
  test:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/cache@v6
        id: pods
        with:
          path: ~/.cache/pods
          key: pods-${{ hashFiles('Podfile.lock') }}
      - if: steps.pods.outputs.cache-hit != 'true'
        run: pod install
      - run: ./scripts/test.sh
```

## What macOS costs after the move

This is the number that decides the migration, because GitHub bills macOS at ten times its Linux rate and rounds every job up to the whole minute. Price it before you move, not after the first invoice.

| Runner | Shape | Per minute |
| --- | --- | --- |
| Namespace, prepaid | 4 vCPU, 7 GB | $0.04 |
| Namespace, prepaid | 6 vCPU, 14 GB | $0.06 |
| GitHub-hosted | 3-core or 4-core, M1 or Intel | $0.062 |
| Blacksmith | macOS M4, 6 vCPU | $0.08 |
| WarpBuild | M4 Pro, 6 vCPU, 14 GB | $0.08 |
| Depot | `depot-macos-latest`, 8 CPU, 24 GB | $0.08 |
| Namespace, overage | 6 vCPU, 14 GB | $0.09 |
| Namespace, prepaid | 12 vCPU, 28 GB | $0.12 |
| Blacksmith | macOS M4, 12 vCPU | $0.16 |
| Latchkey | None | No macOS runner at any price |

> Namespace bills macOS at a 10x multiplier on its unit minutes, which is how its published rate lands under GitHub-hosted at the smallest shape. Prepaid rates require a paid plan; the overage row is what you pay past it. The Blacksmith rows are from blacksmith.sh/pricing and the WarpBuild row from warpbuild.com/pricing, both read 2026-09-20.

## What changes in cache behavior

A Cirrus cache was declarative: you gave it a folder and a fingerprint script, and it decided when to repopulate. `actions/cache` is two things instead of one, a restore and a save, and the repopulate step is yours to guard with `cache-hit`. Forget the guard and the populate step runs on every job, which is the most common way a migrated pipeline gets slower while the cache reports a hit.

The limits are different too. GitHub allows 10 GB per repository and removes entries that have not been accessed for 7 days, and an entry is restorable only from the branch that wrote it or from the default branch. A Cirrus pipeline that shared one cache across every branch will see a cold restore on every new branch after the move.

## What to test first

Move one macOS task before anything else. It is the expensive one, the slow one, and the one most likely to depend on something the Tart image provided that `macos-latest` does not.

1. Translate a single task, push to a branch, and compare the wall clock against the last Cirrus run of the same task.
2. Check the Xcode version. The Cirrus image carried the last three Xcode versions; a GitHub-hosted macOS image carries its own set, so pin the one you need with `xcode-select` rather than assuming.
3. Confirm the cache step logs a restore and that your populate step is skipped on the second run of the same commit.
4. Only then translate the Linux tasks, which are cheaper to get wrong, and delete `.cirrus.yml` last.

## If you are moving to Latchkey

The concession comes first, because it is decisive here: Latchkey has no macOS runner, so the macOS half of a Cirrus migration cannot come to Latchkey at all. Those jobs go to GitHub-hosted, Namespace, WarpBuild or Blacksmith, priced in the table above and in [GitHub Actions runner alternatives](/learn/runners/github-actions-runner-alternatives).

For the Linux half, Latchkey runners are $0.0025 a minute for 2 vCPU with 8 GB, with a dependency cache that is a one-line swap for `actions/cache` and no published size. The label resolves to a 2 vCPU Ubuntu 24.04 runner with 7,734 MB of memory, 43 GB free and Docker 29.7.2, measured on 2026-09-20 in job `cli-02917f33-a216-4255-9f21-7043ddccad19`; the full output is quoted in [migrate from self-hosted runners](/learn/runners/migrate-from-self-hosted-runners). Size it against your `container:` tasks before you translate them, because a Cirrus container that asked for 8 GB will not fit. The containerized tasks are also the ones that fail on registry problems such as [a Docker Hub rate limit](/learn/failures/docker-hub-pull-rate-limit-in-ci), which the runner repairs inside the run rather than billing twice.

## FAQ

### When does Cirrus CI stop running jobs?

On 1 June 2026, from Cirrus Labs' own announcement: "Cirrus CI will shut down effective Monday, June 1, 2026." Cirrus Runners is separate and winds down as existing contracts expire, with no new customers accepted. The scipy issue "CI: Cirrus Runners will disappear soon" (scipy/scipy#24990) is a good example of a project working out which of the two it actually depends on.

### What is the best Cirrus CI alternative for macOS builds?

On published rates read 2026-09-20, Namespace prepaid at $0.04 a minute for 4 vCPU with 7 GB is the lowest, with GitHub-hosted at $0.062 for a 3-core or 4-core machine and WarpBuild and Blacksmith both at $0.08 for a 6 vCPU M4. If you were running Cirrus on your own hardware through a persistent worker, Tart is being relicensed permissively, so self-hosting that stack stays open.

### Why is my GitHub Actions bill mostly macOS minutes?

Because GitHub charges $0.062 a minute for a 3-core or 4-core macOS runner against $0.006 for Linux 2-core x64, and rounds every job up to the whole minute. A macOS job is roughly ten Linux jobs on the invoice, so a pipeline that is 10% macOS by minutes is usually more than half macOS by cost.

### Why do macOS minutes cost 10x Linux minutes?

Apple hardware cannot be oversubscribed the way Linux virtual machines can, and macOS licensing ties the guest to Apple hardware, so the provider is renting you a physical slice rather than a share of a host. Every vendor prices it the same way: Namespace applies an explicit 10x platform multiplier on its unit minutes and publishes the multiplier table.

## References

- [Cirrus Labs: Cirrus CI shutdown announcement and Cirrus Runners wind-down (verified 2026-09-20)](https://cirruslabs.org/)
- [scipy/scipy#24990: CI, Cirrus Runners will disappear soon (verified 2026-09-20)](https://github.com/scipy/scipy/issues/24990)
- [GitHub Actions runner per-minute rates and rounding (verified 2026-09-20)](https://docs.github.com/en/billing/reference/actions-runner-pricing)
- [Namespace pricing: macOS shapes and the platform multiplier (verified 2026-09-20)](https://namespace.so/pricing)
- [Depot runner types: macOS runners at 8 CPUs and 24 GB (verified 2026-09-20)](https://depot.dev/docs/github-actions/runner-types)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
