Here’s the thing: the GitHub Actions macOS 13 deprecation isn’t theoretical anymore. Jobs that use macos-13 are experiencing scheduled brownouts today, November 11, 2025 (14:00–00:00 UTC), again on November 18, and again on November 25. On December 4, 2025, the image is gone for good. If you haven’t moved, expect red builds and late-night firefighting. The good news: M2-powered macOS runners are now generally available, and workflow limits just increased—so you can modernize while you migrate.
What’s actually changing (and when)
Let’s anchor the dates so no one’s guessing in Slack later:
• Brownouts: November 11, 18, and 25, 2025 from 14:00 UTC to 00:00 UTC. During each window, jobs using macos-13, macos-13-large, or macos-13-xlarge can fail by design.
• Retirement: December 4, 2025. macos-13 labels are removed from hosted runners.
• Hardware shift: Apple Silicon (arm64) is the default direction for hosted macOS. M2-powered runners are GA on the xlarge labels: macos-latest-xlarge, macos-15-xlarge, and macos-14-xlarge.
• Bonus change: reusable workflow limits increased to 10 nested calls and 50 total calls per run. That makes refactoring your pipeline easier while you switch labels.
Why this deprecation hurts more than a label rename
Moving off macos-13 is not just a string replace. macOS 13 on hosted runners primarily mapped to Intel (x86_64). Newer images default to Apple Silicon (arm64). That means architecture-sensitive steps—Homebrew installs, Node native modules, Ruby gems with native extensions, Python wheels, iOS simulators, code signing behavior—can behave differently or outright fail. If you’re building cross-platform or shipping iOS apps, you need a deliberate path.
Primary options, with tradeoffs
There are three pragmatic paths I recommend, depending on your workload and timeline:
1) Move to macos-15 (arm64) for most jobs
This is the most future-proof. You get M2 performance on xlarge labels and a modern toolchain. Expect to update some paths, caches, and package steps (details below). For most iOS and Swift projects, this is the right call.
2) Use macos-latest-xlarge for heavy iOS builds
When you need concurrency and speed, the xlarge Apple Silicon runners deliver. They’re ideal for sizable iOS workspaces, Catalyst, SwiftUI previews, and anything with a hefty compile step. Validate simulator availability and the exact Xcode version your team supports; pin it explicitly.
3) Temporary fallback to Intel (paid large) if you must
If you’re blocked by a hard x86_64 dependency (legacy toolchains, brittle native gems), you can use paid Intel-based large runners (macos-14-large and friends) as a short-term bridge. This buys time, not a future. Put a sunset date on this plan.
Migration fast-start: the 90‑minute cutover checklist
Block 90 minutes in your calendar today and work through this sequence in a branch. You’ll flush out the most common breakages fast.
Step 1 — Rename the runner label
Start with the simplest path: swap runs-on to macos-15 or macos-latest (for standard runners) or macos-15-xlarge/macos-latest-xlarge if you want M2 xlarge.
jobs:
build:
runs-on: macos-15 # or macos-latest-xlarge
steps:
- uses: actions/checkout@v4
Tip: if you need to test both architectures temporarily, split the job into a matrix with two labels and compare logs.
Step 2 — Fix Apple Silicon paths and Rosetta usage
On arm64, Homebrew installs to /opt/homebrew (not /usr/local). Update any hardcoded paths, and avoid sudo where not required.
- name: Ensure Homebrew is on PATH
run: echo "/opt/homebrew/bin" >> $GITHUB_PATH
- name: Install tools
run: brew install cocoapods swiftlint
If you must run an x86_64-only binary, run it under Rosetta:
- name: Install Rosetta if needed
run: sudo softwareupdate --install-rosetta --agree-to-license
- name: Run legacy tool under Rosetta
run: arch -x86_64 /usr/local/bin/legacy-tool --help
Use Rosetta sparingly; it’s a stopgap, not a platform strategy.
Step 3 — Pin Xcode and simulators
Don’t rely on “whatever’s on the image.” Pin the Xcode major.minor you support and fail fast if it’s not present. A popular approach is a setup action that switches the active developer dir before build:
- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '15.4' # or the exact version your release team approves
Then select a simulator destination your tests actually target. Print available destinations in logs during the first run to verify:
- name: List destinations
run: xcrun simctl list devices
Step 4 — Revisit caches and derived data
Build artifacts differ across architectures. Clear caches the first time you switch, then rebuild clean to avoid subtle linker or ABI mismatches. For CocoaPods, cache the specs repo separately from derived data to keep cache sizes sane:
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: |
~/.cocoapods/
Pods
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
If you cache DerivedData, include the architecture in the key so Intel and Apple Silicon builds don’t collide.
Step 5 — Audit native extensions and toolchains
Node, Ruby, and Python packages with native code may need to rebuild for arm64. Typical fixes:
• Node: clear node_modules, reinstall with the runner’s Node version, and avoid prebuilt x64-only binaries.
• Ruby: ensure ruby and bundler come from the arm64 toolchain; reinstall gems with native extensions.
• Python: prefer universal wheels or compile from source; pin macosx_12_0_arm64 wheels when available.
People also ask: will my jobs fail during brownouts?
Yes—if a job references macos-13 labels during the brownout windows, it can fail whether the work would otherwise pass. That’s the point of the brownout: force attention before final removal on December 4. Treat these windows as a compliance deadline, not a curiosity.
People also ask: is macos-latest safe to use?
It’s convenient, but “latest” can change under you. If you own a release train or a regulated build, pin the major.minor Xcode and the OS label (macos-15) until you’re ready to move.
People also ask: do M2 xlarge runners include iOS simulators?
Hosted images evolve. Simulators and runtimes can change across image refreshes. That’s why pinning Xcode and printing available destinations is part of the checklist above. If you find your exact runtime missing, install it during the job or adjust the destination to a supported runtime on that image.
The GitHub Actions macOS 13 deprecation meets token and limits changes
This month isn’t just about a runner image. npm’s classic tokens are being permanently revoked on November 19, and GitHub bumped reusable workflow limits to 10 nested/50 total calls. Practically, that means you can refactor your iOS build templating during this migration and roll out more granular CI building blocks across repos—without hitting the old ceilings. For a deeper dive on the npm deadline and impact, see our Nov 19 cutover playbook and the companion post on what will break.
Refactor your pipeline with the new reusable workflow limits
With 10 levels of nested reusable workflows and up to 50 total calls per run, you can extract the noisy bits: provisioning simulators, selecting Xcode, caching pods, notarization, TestFlight upload. Create a reusable workflow per concern and compose them at the app level. Migration is the perfect moment to shrink each job’s YAML and improve reuse across iOS, macOS, and Catalyst targets.
jobs:
ios-build:
uses: your-org/.github/.github/workflows/ios-build.yml@v2
with:
runner-label: macos-15-xlarge
xcode-version: '15.4'
destination: 'platform=iOS Simulator,name=iPhone 15'
Risk radar: what breaks most often on arm64 runners
• Hardcoded Intel paths (/usr/local) and brew formulas compiled for x86_64.
• Native Node modules (think sharp, canvas, sqlite) installed with prebuilt x64 binaries.
• Ruby gems that expect x86_64 (e.g., older ffi or rugged versions).
• Python scientific stacks pinning x86_64 wheels.
• Simulator destination strings that no longer exist on the image.
• Caching artifacts built on Intel and reused on arm64 (ABI mismatch).
Mitigation pattern: make architecture explicit in logs, print uname -m, and key caches by architecture. If you must keep a few Intel-only steps, isolate them to a separate job on an Intel runner while converting the rest to Apple Silicon. Don’t mix architectures inside a single job’s cache.
Concrete example: converting a React Native iOS job
React Native teams catch all the sharp edges at once: Node native modules, CocoaPods, Xcode, simulators. Here’s a minimal Apple Silicon-friendly sequence:
jobs:
build-ios:
runs-on: macos-15-xlarge
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Ensure Homebrew path
run: echo "/opt/homebrew/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
brew install cocoapods
npm ci
- name: Cache Pods
uses: actions/cache@v4
with:
path: |
~/.cocoapods/
ios/Pods
key: ${{ runner.os }}-${{ runner.arch }}-pods-${{ hashFiles('ios/Podfile.lock') }}
- name: Pod install
run: |
cd ios
pod install --repo-update
- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '15.4'
- name: Build
run: |
cd ios
xcodebuild -workspace App.xcworkspace -scheme App -sdk iphonesimulator -configuration Release -derivedDataPath build
This flow avoids Intel-only paths, pins Xcode, and isolates caches per-arch. Use a separate job for notarization or App Store upload if those steps rely on legacy tooling.
Governance and visibility: don’t skip the org-level sweep
Even if you fix your main app, old workflows lurk in utility repos. Do an org-wide search for the soon-to-be-invalid labels (macos-13, macos-13-large, macos-13-xlarge). If you manage multiple teams, publish a short internal advisory with the exact brownout times and the label replacements you want engineers to standardize on.
While you’re in there, take advantage of finer-grained Actions permissions and custom repository roles to reduce blast radius. Decoupling “can run workflows” from “can manage runners/secrets” is worth the 15-minute review.
What about GitHub-hosted Intel needs long term?
Apple Silicon is where all the investment is now. If you truly need x86_64 for a longer period, budget for paid Intel large runners or move those jobs to self-hosted Intel hardware. Place an end date on that exception so you don’t carry a permanent tax.
What to do next (today)
• Change runs-on from macos-13 to macos-15 or macos-latest-xlarge in your most critical workflow.
• Pin Xcode and print simulator destinations to validate the image.
• Fix brew paths and rebuild native dependencies on arm64.
• Clear and rekey caches by architecture; run a clean build.
• Sweep your org for deprecated labels, and standardize replacements.
• Create or update a reusable workflow for iOS build/test so other repos inherit the fix.
• If you publish packages, schedule your npm token migration before November 19—use our short checklist and the deeper cutover playbook.
Need help untangling CI, fast?
We help teams ship under deadlines. If you want a pragmatic audit and a clean migration plan for Apple Silicon, nested workflows, secrets, and caching, start here: our engineering services overview, recent work samples, and the latest posts on the Bowu Blog. If you’re staring at a red dashboard right now, reach out via Contacts—we’ll get you a same-day slot.
Final thought: make this migration your spring cleaning
The GitHub Actions macOS 13 deprecation forces the move you were likely delaying anyway: embrace Apple Silicon, pin your toolchains, and modularize with reusable workflows. Use the next two weeks to land the changes, and you’ll head into December with faster builds, fewer flaky steps, and a pipeline you can explain on a whiteboard in five minutes. That’s not busywork—that’s operational leverage.
