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 buildThe 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 buildThe 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.ymlSearch for:
macos-14You can use a command such as:
grep -R "macos-14" .github/workflowsOn 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-14may 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-15But 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 ReleaseThe 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-toolThis 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: 22For .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 ReleasePrinting 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-14to 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 --versionFor .NET:
- name: .NET information
run: dotnet --infoThis 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 foundThe 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 --versionThen inspect dependency installation.
If a command is missing, determine whether it should be:
Installed during the workflow.
Configured through an official setup action.
Included in a custom container or environment.
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 ReleaseThe 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
Search every workflow for the retired runner label.
Check matrix configurations as well as direct
runs-onvalues.Move to a supported macOS runner before the old image is unavailable.
Explicitly configure important SDK and runtime versions.
Record toolchain versions in CI logs during migration.
Test builds, tests, packaging, signing, and deployment separately.
Review scheduled and release workflows, not just pull request workflows.
Test the replacement runner in a branch before changing the main workflow.
Avoid relying on undocumented preinstalled tools.
Keep the migration reversible until the new pipeline is stable.
Review native dependencies and Apple-specific tooling carefully.
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.

Join the conversation! Your thoughts help the community grow.