iOS Simulator Runtimes on macOS GitHub Actions Runners

See which iOS, tvOS, watchOS, and visionOS simulator runtimes each WarpBuild macOS image carries, per-minute prices, and how to assert a runtime in a job.

Last verified:

A GitHub Actions job can only boot a simulator whose runtime is already installed on the runner image, so the runtimes available to xcodebuild test are decided by the macOS label in runs-on. WarpBuild macOS runners carry the simulator runtimes bundled with the Xcode versions in each image, and the two macOS 26 labels additionally carry the iOS 27.0, tvOS 27.0, watchOS 27.0, and visionOS 27.0 runtimes that ship with Xcode 27.0.

This page lists the runtime coverage of each macOS image, gives the per-minute rate for every macOS label, prices the minutes a job spends downloading a missing runtime, and shows the assertion step that turns a missing runtime into a fast, readable failure instead of a confusing xcodebuild error.

Catalog

WarpBuild provides Linux x64, Linux ARM64, macOS, and Windows runners. The macOS runners are Apple Silicon on ARM64, in a 6 vCPU shape and a 12 vCPU shape across five warp- labels.

LabelmacOS imagevCPUMemoryStoragePrice per minute
warp-macos-26-arm64-6xmacOS 26622 GB120GB SSD$0.080
warp-macos-26-arm64-12xmacOS 261244 GB270GB SSD$0.160
warp-macos-15-arm64-6xmacOS 15622 GB120GB SSD$0.080
warp-macos-15-arm64-12xmacOS 151244 GB270GB SSD$0.160
warp-macos-14-arm64-6xmacOS 14622 GB120GB SSD$0.080

Runtime coverage by image

Every WarpBuild macOS image is built from the corresponding upstream GitHub runner image and carries the same tooling, so the resident runtimes on an image are the ones bundled with that image's Xcode versions. The macOS 26 image is the one with an addition on top.

macOS imageLabelsiOStvOSwatchOSvisionOSPer-build inventory
macOS 26warp-macos-26-arm64-6x, warp-macos-26-arm64-12x27.0 (24A5355p) plus the runtimes in the upstream image27.0 (24J5289o) plus the runtimes in the upstream image27.0 (24R5289n) plus the runtimes in the upstream image27.0 (24M5291p) plus the runtimes in the upstream imagemacos-26-arm64 readme and the macOS 26 tooling table
macOS 15warp-macos-15-arm64-6x, warp-macos-15-arm64-12x (aliases warp-macos-latest-arm64-6x, warp-macos-latest-arm64-12x)Bundled with the image Xcode setBundled with the image Xcode setBundled with the image Xcode setBundled with the image Xcode set where that Xcode carries a visionOS runtimemacos-15-arm64 readme
macOS 14warp-macos-14-arm64-6xBundled with the Xcode 15 line, 15.4 defaultBundled with the Xcode 15 lineBundled with the Xcode 15 lineBundled with the Xcode 15 line where that Xcode carries a visionOS runtimemacos-14-arm64 readme

The readme files linked in that table are the authoritative per-build inventory. GitHub regenerates them on every image release, so an exact runtime build string for the macOS 14 and macOS 15 images belongs in the readme rather than in a page that refreshes on a 90 day cadence. The preinstalled software docs keep the readme link for each WarpBuild image in one place, and the cloud runner docs carry the label, size, and rate for every macOS shape.

The macOS 26 image ships the Xcode 27.0 SDKs and simulator runtimes while GitHub's upstream macOS 27 runner image is in beta, and a dedicated macOS 27 image follows once that image is released. Xcode 27.0 on macOS 26 is maintained until then, so a workflow that pins a /Applications/Xcode_27.0.app path needs one edit when the switch happens.

Two aliases sit on top of the versioned labels. warp-macos-latest-arm64-6x resolves to warp-macos-15-arm64-6x and warp-macos-latest-arm64-12x resolves to warp-macos-15-arm64-12x, in sync with GitHub's macos-latest tag. A job that needs the 27.0 runtimes names the macOS 26 label directly, because the alias moves when the upstream tag moves.

Three constraints shape how much runtime coverage a job can lean on. macOS 13 runners were removed on June 8, 2026, so workflows still naming a macOS 13 label queue without a match. macOS runners do not support nested virtualization and cannot run Docker, so container steps in an iOS pipeline belong in a Linux job. Runner storage is ephemeral and is deleted when the runner terminates, so a downloaded runtime never survives to the next job: the download repeats on every run that needs it.

Pricing

macOS rates are flat per minute by size and do not change with the image or with the number of runtimes on it. A job on macOS 26 and a job on macOS 14 at the same vCPU count cost the same.

ConfigurationLabel or GitHub shapevCPUMemoryPrice per minute30-minute job
WarpBuild 6 vCPUwarp-macos-26-arm64-6x, warp-macos-15-arm64-6x, warp-macos-14-arm64-6x622 GB$0.080$2.40
WarpBuild 12 vCPUwarp-macos-26-arm64-12x, warp-macos-15-arm64-12x1244 GB$0.160$4.80
GitHub-hosted largest macOS ARM64macos_xl514 GB$0.102$3.06

warp-macos-latest-arm64-6x (6 vCPU, 22 GB) costs $0.08 per minute against $0.102 per minute for the largest GitHub-hosted macOS ARM64 runner (5 vCPU, 14 GB): 22 percent lower list price. GitHub list price checked on 2026-08-13, from GitHub's published Actions minute multipliers. WarpBuild rates come from the pricing page.

Pricing is purely usage based. There is no base subscription fee, no platform fee, and no seat fee. Billing is per minute of runner time, which is what makes a runtime download a line on the invoice rather than a free convenience.

What a runtime download costs at job time

Time the download and install step in your own job log first, then read across. Each row is a step duration you supply, and the arithmetic is that duration times the macOS rate.

Download and install stepCost per job at $0.080200 jobs per month800 jobs per month
3 minutes$0.24$48.00$192.00
6 minutes$0.48$96.00$384.00
10 minutes$0.80$160.00$640.00

On the 12 vCPU shape at $0.160 per minute, every figure in that table doubles: a 6-minute step is $0.96 per job and $192.00 across 200 jobs. A simulator runtime is a multi-gigabyte download, so the step also competes for the 120GB disk on the 6 vCPU shape. Matrices that need several runtimes in one job belong on the 12 vCPU shape with 270GB.

The cheapest version of this line is not having it. Pinning a label whose image already carries the runtime you test against drops the download step, and the job pays for compile and test minutes only. Signup includes $10 free credits, which covers a few hundred minutes of macOS runner time while you measure the two shapes against your own test suite.

Configuration

Two edits put a simulator job on a WarpBuild macOS runner with a runtime guarantee: the runs-on label, and an assertion step that fails early when the runtime is absent.

Assert the runtime before the test step

name: ios-simulator-tests
on:
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: warp-macos-26-arm64-6x
    env:
      DEVELOPER_DIR: /Applications/Xcode_27.0.app/Contents/Developer
      RUNTIME_ID: com.apple.CoreSimulator.SimRuntime.iOS-27-0
      SIMULATOR_NAME: iPhone 16 Pro
    steps:
      - uses: actions/checkout@v4

      - name: Record the toolchain and runtimes in the job log
        run: |
          xcodebuild -version
          xcrun simctl list runtimes

      - name: Assert the iOS 27.0 runtime is installed
        run: |
          if ! xcrun simctl list runtimes --json | grep -q "$RUNTIME_ID"; then
            echo "::error::$RUNTIME_ID is not on this image"
            exit 1
          fi

      - name: Test
        run: |
          xcodebuild test \
            -scheme AppScheme \
            -destination "platform=iOS Simulator,name=$SIMULATOR_NAME,OS=27.0" \
            -resultBundlePath TestResults.xcresult

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: TestResults.xcresult

Matching on the runtime identifier rather than the display name is what makes the assertion reliable: com.apple.CoreSimulator.SimRuntime.iOS-27-0 is stable across builds of the same runtime, while the display string carries a build suffix that changes. The xcrun simctl list runtimes step above it costs a second and puts the whole inventory in the log, so an image update that adds or removes a runtime reads as a diff between two runs instead of a test failure someone has to bisect. Device names are a separate axis: confirm SIMULATOR_NAME against xcrun simctl list devicetypes on the label you pin.

Download a runtime only when it is missing

      - name: Ensure the iOS runtime is present
        run: |
          if ! xcrun simctl list runtimes --json | grep -q "$RUNTIME_ID"; then
            echo "Runtime missing on this image, downloading"
            time xcodebuild -downloadPlatform iOS
          fi

The guard matters more than the download. Wrapping the download in the same identifier check means the step is a no-op on images that already carry the runtime, and the time prefix writes the duration into the log, which is the number to put in the cost table above. Running xcodebuild -downloadAllPlatforms instead pulls every platform and multiplies both the minutes and the disk use.

DEVELOPER_DIR selects the toolchain for xcodebuild, swift, and every xcrun call in the steps that follow, without sudo and without changing the machine-wide default. Pin it to the bundle that carries the runtime you assert on, so the OS= value in the destination string and the SDK the tests compile against stay in sync.

Start from the macOS runner hub for the full lineup, read what simulator runtimes ship on macOS runners for the short version of this page, and see the iOS simulator test guide for the surrounding pipeline. Term definitions live in the simulator runtime glossary entry.

FAQ

Which simulator runtimes ship on WarpBuild macOS runners?

Each macOS image carries the simulator runtimes bundled with the Xcode versions in the upstream GitHub image it is built from. The two macOS 26 labels, warp-macos-26-arm64-6x and warp-macos-26-arm64-12x, additionally carry iOS 27.0 (24A5355p), tvOS 27.0 (24J5289o), watchOS 27.0 (24R5289n), and visionOS 27.0 (24M5291p), which ship with Xcode 27.0 (build 27A5194q).

How do I check that a runtime exists before the test step runs?

Run xcrun simctl list runtimes as an early step and fail the job when the runtime you need is missing. Matching on the runtime identifier, such as com.apple.CoreSimulator.SimRuntime.iOS-27-0, is more precise than matching on the display name, and the full listing in the log makes an image change readable as a diff.

What does downloading a simulator runtime inside a job cost?

Runner time at the macOS rate. On the 6 vCPU shape at $0.080 per minute, a 6-minute download and install step adds $0.48 per job, which is $96.00 across 200 jobs in a month. Pinning a label whose image already carries the runtime removes the step and its cost.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.