GitHub Actions runners are part of the build infrastructure for many development teams. When GitHub retires an operating system image, the change can affect CI pipelines even when nobody has modified the workflow itself.

macOS runners are especially important for projects that build Apple applications, run platform-specific tests, package software, or maintain cross-platform projects that include macOS as part of their CI matrix.

The retirement of the macOS 14 runner image is therefore more than an operating system upgrade. It is a reminder that CI environments are dependencies too.

A workflow can be perfectly valid YAML and still fail because the runner image no longer exists, a preinstalled tool changed, or the build environment behaves differently.

The safest approach is to treat runner-image changes like any other infrastructure migration.

What Is a GitHub Actions Runner Image?

When a GitHub Actions workflow runs on a hosted runner, GitHub provides a virtual machine with an operating system and a collection of preinstalled development tools.

A workflow may specify a runner such as:

jobs:
  build:
    runs-on: macos-14

    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: dotnet build

The runs-on value determines the runner environment.

That environment contains much more than macOS itself.

It can include:

  • Development SDKs

  • Compilers

  • Package managers

  • Build utilities

  • Shell tools

  • Runtime versions

  • System libraries

  • Platform-specific tooling

When the runner image changes, some of those components can also change.

Why Runner Retirement Matters

A common assumption is:

The workflow uses macOS, so any macOS runner should work.

That is not always true.

Consider a workflow that depends on a specific SDK:

runs-on: macos-14

steps:
  - uses: actions/checkout@v4

  - name: Build
    run: dotnet build

The workflow may have worked for months.

When macos-14 is retired, the workflow needs to move to a supported runner image.

That change can also expose hidden assumptions about:

  • SDK versions

  • Xcode versions

  • Ruby versions

  • Python versions

  • Node.js versions

  • Package managers

  • Native dependencies

  • Build scripts

The operating system is only one part of the build environment.

The First Thing to Check

Start by searching all workflow files for macOS runner labels.

For example:

.github/
└── workflows/
    ├── build.yml
    ├── test.yml
    ├── release.yml
    └── ios.yml

Search for:

macos-14

You can use a command such as:

grep -R "macos-14" .github/workflows

On Windows, PowerShell can be used:

Get-ChildItem .github/workflows -Recurse |
    Select-String "macos-14"

Do not search only the main build workflow.

Release, deployment, test, packaging, and scheduled workflows can all use a macOS runner.

Review Matrix Builds

The runner may also be hidden inside a build matrix.

For example:

strategy:
  matrix:
    os:
      - ubuntu-latest
      - windows-latest
      - macos-14

runs-on: ${{ matrix.os }}

In this case, simply searching for a direct:

runs-on: macos-14

may not find every affected workflow.

A migration review should therefore include matrix definitions.

Choose the Replacement Carefully

The obvious migration is to replace the retired image with a supported macOS runner.

For example:

runs-on: macos-15

But do not make a blind search-and-replace across a large organization.

First check what the project actually requires.

For example, an application might depend on a particular Xcode version.

The workflow should make that dependency explicit where possible.

A better pattern is:

steps:
  - uses: actions/checkout@v4

  - name: Select required toolchain
    run: |
      # Configure the required build toolchain here.

  - name: Build
    run: dotnet build --configuration Release

The exact setup depends on the technology being built.

Do Not Depend on Preinstalled Software

One of the biggest problems with hosted runners is assuming that every tool will always be available in the same version.

For example:

steps:
  - name: Build
    run: some-build-tool

This assumes that the required tool is already installed and available in PATH.

A more reliable workflow explicitly installs or selects the required version when practical.

For Node.js:

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: 22

For .NET:

- name: Setup .NET
  uses: actions/setup-dotnet@v4
  with:
    dotnet-version: '8.0.x'

The versions should match the project's actual support requirements.

This reduces the dependency on whatever happens to be preinstalled on a particular runner image.

Why Pinning Tool Versions Helps

Suppose a workflow relies on the default Node.js version installed on a runner.

Today it might receive one version.

After an image migration, it could receive another.

That can create failures that look unrelated to the runner change.

Explicit setup makes the dependency visible:

- uses: actions/setup-node@v4
  with:
    node-version: '22.x'

Now the operating-system migration and runtime-version decision are separate concerns.

This is easier to maintain.

Review Xcode Dependencies

macOS workflows often have another dependency that Linux and Windows projects do not: Apple development tooling.

A project may depend on:

  • Xcode

  • Swift

  • Objective-C

  • iOS SDKs

  • macOS SDKs

  • CocoaPods

  • Fastlane

  • Code-signing tools

If the project builds Apple software, test the complete build after moving to the replacement runner.

For example:

jobs:
  ios-build:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - name: Show Xcode version
        run: xcodebuild -version

      - name: Build
        run: |
          xcodebuild \
            -scheme MyApp \
            -configuration Release

Printing the toolchain version is useful during migration because CI logs then show exactly which environment performed the build.

Test Before Changing Production Workflows

A safe migration does not immediately replace every production workflow.

Create a test branch first.

Change:

runs-on: macos-14

to the replacement runner.

Then run:

  • Build

  • Unit tests

  • Integration tests

  • Packaging

  • Signing

  • Deployment checks

For an application that publishes artifacts, verify the actual output rather than checking only whether the workflow completed successfully.

Compare the Old and New Environment

If you are debugging a migration, collect environment information.

For example:

- name: Environment information
  run: |
    sw_vers
    xcodebuild -version
    node --version
    npm --version
    python3 --version

For .NET:

- name: .NET information
  run: dotnet --info

This helps identify differences between the old and new environments.

A build failure is easier to understand when the CI logs clearly show which SDK and compiler versions were used.

Common Migration Failures

Missing Command

A workflow may fail with:

command not found

The command may no longer be installed on the new image or may not be available in the expected path.

Install or configure the required tool explicitly.

Different SDK Version

A compiler or runtime update can expose warnings or errors that did not appear before.

Check the tool version first.

Dependency Installation Failure

Native dependencies can behave differently after an operating-system image migration.

Review package-manager output and native build logs.

Signing Problems

Apple signing workflows can fail because of certificate, provisioning, keychain, or toolchain differences.

Do not assume a successful compile means the signing process will also work.

Test Differences

Tests that interact with the operating system can behave differently between runner images.

Check platform-specific tests separately.

Common Mistakes

Only Updating runs-on

Changing the runner label is necessary, but it may not be sufficient.

The new image can have different tool versions and system behavior.

Depending on Latest Versions

Using whatever happens to be preinstalled makes builds harder to reproduce.

Explicitly configure important toolchain versions.

Forgetting Scheduled Workflows

A repository may have scheduled workflows that nobody runs manually.

Search all workflow files.

Testing Only Compilation

A build can succeed while packaging, signing, or deployment fails.

Test the entire pipeline.

Migrating Without a Rollback Plan

If a production release depends on the workflow, keep a clear migration and rollback strategy.

Troubleshooting the New Runner

When the new runner fails, start with the environment.

Add:

- name: Environment diagnostics
  run: |
    sw_vers
    uname -a
    xcodebuild -version
    node --version
    python3 --version

Then inspect dependency installation.

If a command is missing, determine whether it should be:

  1. Installed during the workflow.

  2. Configured through an official setup action.

  3. Included in a custom container or environment.

  4. Replaced with another supported tool.

Do not immediately assume GitHub's runner is broken.

Often the workflow was relying on an undocumented environment assumption.

A More Maintainable Workflow

A maintainable workflow separates operating-system selection from toolchain configuration.

For example:

name: Build

on:
  push:
  pull_request:

jobs:
  build:
    runs-on: macos-15

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Show environment
        run: |
          sw_vers
          dotnet --info

      - name: Restore
        run: dotnet restore

      - name: Build
        run: dotnet build --no-restore --configuration Release

      - name: Test
        run: dotnet test --no-build --configuration Release

The important pattern is that the workflow explicitly declares important dependencies.

Advantages of Migrating Early

Fewer Emergency Fixes

Moving before the retirement deadline gives developers time to investigate failures.

Better Build Reproducibility

Explicit tool versions reduce dependence on runner-image defaults.

Cleaner CI Configuration

Migration is a good opportunity to remove unnecessary assumptions from workflows.

Easier Future Upgrades

Once the workflow explicitly configures its toolchain, future runner migrations become less disruptive.

Disadvantages and Trade-Offs

Migration Takes Time

Every macOS workflow needs testing.

Toolchain Differences Can Surface Hidden Problems

A newer image may reveal issues that were previously hidden.

Platform-Specific Projects Need More Testing

Apple builds, signing workflows, and native dependencies require particular attention.

Temporary Parallel Maintenance

Teams may need to test the old and new environments during migration.

Best Practices

  1. Search every workflow for the retired runner label.

  2. Check matrix configurations as well as direct runs-on values.

  3. Move to a supported macOS runner before the old image is unavailable.

  4. Explicitly configure important SDK and runtime versions.

  5. Record toolchain versions in CI logs during migration.

  6. Test builds, tests, packaging, signing, and deployment separately.

  7. Review scheduled and release workflows, not just pull request workflows.

  8. Test the replacement runner in a branch before changing the main workflow.

  9. Avoid relying on undocumented preinstalled tools.

  10. Keep the migration reversible until the new pipeline is stable.

  11. Review native dependencies and Apple-specific tooling carefully.

  12. Document the runner and toolchain requirements for the project.

Summary

GitHub Actions macOS runner retirement can affect more than the operating-system label in a workflow. Changes to the runner image can also expose assumptions about SDKs, compilers, runtimes, native dependencies, and platform-specific tools.

Developers should search all workflows and matrices for the retired runner, move to a supported macOS image, explicitly configure important tool versions, and test the complete CI/CD process.

For Apple projects, pay particular attention to Xcode, signing, SDKs, and native dependencies. For cross-platform projects, verify that the macOS build remains consistent with Linux and Windows builds.

The goal should not be simply to replace macos-14. The goal is to make the CI environment explicit enough that future runner-image changes are predictable rather than unexpected.