Can You Run Docker on macOS GitHub Actions Runners?
No. macOS GitHub Actions runners cannot run Docker and have no nested virtualization. Send container steps to a Linux warp- label or a remote Docker builder.
Last verified:
No. macOS GitHub Actions runners cannot run Docker, because they do not support nested virtualization and so cannot host the Linux virtual machine that Linux containers need (WarpBuild cloud runners documentation, checked on 2026-08-13). The working pattern is a split workflow: the macOS job handles Xcode, Swift, and simulator work, and a second job on a Linux warp- label or a remote Docker builder handles the container work.
Answer
The limit sits below the runner image. A macOS runner is itself a virtualized guest on Apple Silicon hardware. Docker on macOS runs Linux containers inside a Linux virtual machine, so a Docker daemon on that runner would need a second layer of virtualization inside the first. That second layer is nested virtualization, and the macOS fleet does not have it.
The documented statement is short: macOS runners do not support nested virtualization and cannot run Docker (cloud runners documentation). The same constraint applies to GitHub-hosted macOS runners, which run macOS on virtualized Apple Silicon as well (GitHub-hosted runners reference, checked on 2026-08-13).
Four things fail on a macOS job as a result:
- Any step that shells out to
docker build,docker run,docker compose, ordocker login. - The
container:job key, which asks GitHub Actions to run every step inside a container. - The
services:block, which starts sidecar containers such as Postgres or Redis for the job. - Container actions, meaning actions whose
action.ymldeclaresruns.using: docker.
Items 2, 3, and 4 are Linux-only in GitHub Actions by design, independent of the runner provider (GitHub Actions workflow syntax reference). JavaScript and composite actions run fine on macOS, which covers actions/checkout, the language setup actions, and the artifact actions.
Two configurations replace the missing daemon, and both keep everything in one workflow file.
Run container work on a Linux warp- label in the same workflow. Add a second job with runs-on: warp-ubuntu-latest-x64-4x. It gets a normal Docker daemon, so services:, container:, and every docker command behave as they do on any Linux runner. The two jobs run in parallel unless one declares needs on the other.
Build images on a remote Docker builder while the macOS job handles Apple toolchain work. Remote Docker builders are dedicated build virtual machines that live outside the runner and keep a persistent layer cache on local disk (Docker builders documentation). The job that drives the build stays on a Linux runner, the build itself executes on the builder, and repeat builds reuse cached layers instead of rebuilding them.
Here is the split, with real labels:
name: release
on:
push:
tags: ["v*"]
jobs:
ios-archive:
runs-on: warp-macos-26-arm64-6x
steps:
- uses: actions/checkout@v4
- name: Record the selected toolchain
run: |
xcodebuild -version
xcrun simctl list runtimes
- name: Archive the app
run: |
xcodebuild -scheme Example \
-destination "generic/platform=iOS" \
-archivePath build/Example.xcarchive \
archive
- uses: actions/upload-artifact@v4
with:
name: ios-archive
path: build/Example.xcarchive
api-image:
runs-on: warp-ubuntu-latest-x64-4x
services:
postgres:
image: postgres:17
env:
POSTGRES_PASSWORD: test
ports: ["5432:5432"]
steps:
- uses: actions/checkout@v4
- name: Integration tests against the service container
run: ./scripts/integration-tests.sh
env:
DATABASE_URL: postgres://postgres:test@localhost:5432/postgres
- name: Build and push the API image
uses: Warpbuilds/build-push-action@v6
with:
context: ./services/api
push: true
tags: ghcr.io/acme/api:${{ github.ref_name }}
profile-name: "api-builder"The ios-archive job never sees a container, and the api-image job never sees Xcode. Nothing in the macOS job needs a Docker daemon, and the container work runs where containers work.
Pricing on WarpBuild is purely usage based.
Detail
What macOS runners are for
WarpBuild provides Linux x64, Linux ARM64, macOS, and Windows runners. The macOS fleet exists for the work that has to touch the Apple toolchain: xcodebuild, Swift package resolution, simulator runs, code signing, notarization, and archive export. Those steps cannot run on Linux, which is why the platform stays in the catalog even though it carries no container support.
Five labels cover three macOS releases, in two sizes. Every row below comes from the cloud runners documentation, checked on 2026-08-13.
| Runner label | OS | vCPU | Memory | Storage | Per minute | Alias |
|---|---|---|---|---|---|---|
warp-macos-26-arm64-6x | macOS 26 | 6 | 22GB | 120GB SSD | $0.08 | none |
warp-macos-26-arm64-12x | macOS 26 | 12 | 44GB | 270GB SSD | $0.16 | none |
warp-macos-15-arm64-6x | macOS 15 | 6 | 22GB | 120GB SSD | $0.08 | warp-macos-latest-arm64-6x |
warp-macos-15-arm64-12x | macOS 15 | 12 | 44GB | 270GB SSD | $0.16 | warp-macos-latest-arm64-12x |
warp-macos-14-arm64-6x | macOS 14 | 6 | 22GB | 120GB SSD | $0.08 | none |
Multiple sizes and configurations exist per chip. The 12x configuration doubles the cores, doubles the memory, and takes disk from 120GB to 270GB, which matters on repositories where derived data, resolved Swift packages, and several simulator runtimes share one disk. The macOS 26 labels ship Xcode 27.0 with its iOS, tvOS, watchOS, and visionOS 27.0 simulator runtimes on top of the upstream GitHub macOS 26 image, while GitHub's upstream macOS 27 runner image is in beta; a dedicated macOS 27 image follows once that image is released.
On price, 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 in the GitHub Actions billing reference. The full label list with per-size arithmetic is on the macOS runner catalog page.
Remote Docker builders are the piece that matters here, because they carry the container work a macOS job cannot.
Nested virtualization is available on Linux x64 only
Nested virtualization and /dev/kvm access exist on the Linux x64 fleet through the nested-virtualization.enabled=true dynamic label (nested virtualization documentation). That label is what Android emulator jobs and similar workloads use. It has no macOS equivalent, and it has no Linux ARM64 equivalent either.
The practical consequence for a mobile repository is that iOS simulator tests and Android emulator tests land on different runners. Simulator tests stay on a macOS label because the simulator is part of Xcode. Emulator tests go to a Linux x64 label with the nested virtualization flag set. Neither job needs Docker to be present on the macOS machine.
Passing work between the macOS job and the container job
Two jobs do not share a filesystem, so anything the container job needs from the macOS job travels as an artifact. actions/upload-artifact in the macOS job and actions/download-artifact in the Linux job cover the common cases: a built .ipa, a .xcarchive, a dSYM bundle, or a test report that a later job packages into an image or uploads to a release.
Order is a choice. Leave needs off and the jobs run in parallel, so the pipeline takes the length of the longer job. Add needs: ios-archive to the container job and the pipeline runs them in sequence, which is what you want when the image being built has to contain an artifact the macOS job produced.
Runner storage on both platforms is ephemeral and is deleted when the runner terminates, so any state that has to outlive a job goes to an artifact, a cache, or a registry.
Building the image on a remote Docker builder
A builder profile maps to one dedicated build virtual machine with a persistent layer cache. Multiple jobs can share a profile and run on it in parallel, and profiles carry amd64, arm64, and multi-architecture options (Docker builders documentation). The Warpbuilds/build-push-action action is a drop-in replacement for the standard build and push action: point it at a profile with profile-name and drop the local buildx setup step.
Builders also work from runners outside WarpBuild, using an API key in api-key, which is useful when part of a pipeline already runs somewhere else. The Docker builds on GitHub Actions page covers the full configuration, and the remote Docker builder catalog lists the profile sizes and their rates.
A worked monthly cost model
Take a repository that ships an iOS app and the API image behind it, running 400 pipelines a month. The macOS job takes 14 minutes and the container job takes 6 minutes. Every WarpBuild rate comes from the cloud runners documentation and every GitHub rate from the GitHub Actions billing reference, both checked on 2026-08-13.
| Job | Runner | Minutes per run | Rate per minute | Monthly at 400 runs |
|---|---|---|---|---|
| Xcode archive | warp-macos-26-arm64-6x | 14 | $0.08 | $448.00 |
| Image build and integration tests | warp-ubuntu-latest-x64-4x | 6 | $0.008 | $19.20 |
| Total | $467.20 |
The same two jobs on GitHub-hosted runners, at GitHub's published list prices:
| Job | GitHub-hosted runner | Minutes per run | Rate per minute | Monthly at 400 runs |
|---|---|---|---|---|
| Xcode archive | largest macOS ARM64 runner, 5 vCPU and 14 GB | 14 | $0.102 | $571.20 |
| Image build and integration tests | 4-core Linux larger runner | 6 | $0.012 | $28.80 |
| Total | $600.00 |
The gap is $132.80 a month and $1,593.60 over twelve months, which works out to 22 percent lower list price on the same job shapes (GitHub list prices, checked on 2026-08-13). The macOS row also buys a larger machine: 6 vCPU and 22 GB against 5 vCPU and 14 GB.
Moving the image build to a remote Docker builder changes the second row rather than the first. A 3 minute build on the 16 vCPU, 32 GB, 100GB disk profile at $0.06 per minute (pricing page, checked on 2026-08-13) costs 400 x 3 x $0.06 = $72.00 a month, and it comes with a layer cache that persists across every job sharing that profile. Whether that trade pays depends on how much of the Dockerfile is dependency installation, which is the part a warm layer cache skips.
Related Questions
Can one GitHub Actions job use a macOS runner and a Docker step together?
No. A job runs on exactly one runner, and a macOS runner has no Docker daemon and no nested virtualization. Split the work into two jobs in the same workflow file, one on a macOS label such as warp-macos-26-arm64-6x and one on a Linux label such as warp-ubuntu-latest-x64-4x.
How do I structure a workflow that needs both an Xcode build and a container build?
Declare two jobs. The macOS job runs xcodebuild and uploads its archive with actions/upload-artifact. The Linux job builds and pushes the image. They run in parallel unless one declares needs on the other, so the pipeline takes the length of the longer job rather than the sum. The Docker builds on GitHub Actions page covers the image half in detail.
Can a macOS job use services or a job container for integration tests?
No. The services: block and the container: job key run on Linux runners. Move those tests to a Linux job, or have the macOS job talk to a service started by a Linux job that runs earlier in the workflow.
Do GitHub-hosted macOS runners run Docker?
No. Nested virtualization is unavailable on macOS runners on both fleets, so macOS runners on GitHub and on WarpBuild alike lack a working Docker daemon. See the GitHub-hosted runners reference, checked on 2026-08-13.
Pick a macOS label from the macOS runner catalog, check what else the platform covers in whether GitHub Actions runners support macOS, and price the split against your own job minutes on the pricing page.
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.