Skip to content

feat: add support for macos runners#4930

Merged
Brend-Smits merged 46 commits into
github-aws-runners:mainfrom
edersonbrilhante:feat-macos-support
Jun 10, 2026
Merged

feat: add support for macos runners#4930
Brend-Smits merged 46 commits into
github-aws-runners:mainfrom
edersonbrilhante:feat-macos-support

Conversation

@edersonbrilhante

@edersonbrilhante edersonbrilhante commented Dec 3, 2025

Copy link
Copy Markdown
Contributor

Summary

This PR adds support for provisioning GitHub Actions runners on macOS EC2 instances by wiring placement group configuration through the multi-runner module and introducing macOS-specific bootstrap scripts.

Motivation

macOS EC2 instances require a dedicated host (host_id), which is managed through placement groups.
This work prepares the module to request mac-based EC2 runners by consuming placement configuration as defined in #4929

This PR closes #2070 #2069

Test Plan:

  • deploy examples/dedicated-mac-hosts
  • update file examples/multi-runner/templates/runner-configs/mac-arm64-26.yaml with host_resource_group_arn
    and license_configuration_arn
  • deploy examples/multi-runner

@edersonbrilhante

Copy link
Copy Markdown
Contributor Author

@npalm what do you think?

I didn't test yet. I gonna test during this week.

@npalm

npalm commented Dec 15, 2025

Copy link
Copy Markdown
Member

Sorry had no time yet to dig in.

@edersonbrilhante edersonbrilhante changed the title feat: add support to request macos runners feat: add support for macos runners Dec 16, 2025
@edersonbrilhante edersonbrilhante force-pushed the feat-macos-support branch 2 times, most recently from 41912ad to e142885 Compare January 7, 2026 10:50
@edersonbrilhante edersonbrilhante marked this pull request as ready for review January 7, 2026 10:54
@edersonbrilhante edersonbrilhante requested review from a team as code owners January 7, 2026 10:54
@edersonbrilhante

Copy link
Copy Markdown
Contributor Author

@npalm I think the code is ready for review.

@edersonbrilhante

Copy link
Copy Markdown
Contributor Author

@koendelaat @guicaulada When the time permits can you also take a look?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enables support for macOS runners on EC2 instances by adding "osx" as a valid runner_os option alongside "linux" and "windows". The changes include macOS-specific bootstrap scripts, placement group and license specification configuration for dedicated Mac hosts, and updated documentation.

Changes:

  • Added "osx" as a valid value for runner_os variables across modules with corresponding validation updates
  • Introduced macOS-specific user-data and runner installation/startup scripts
  • Added license_specifications variable support through the module hierarchy for Mac dedicated host licensing requirements
  • Created a new example module for provisioning dedicated Mac hosts with resource groups and license configurations

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
variables.tf Added osx to runner_os validation and updated description
modules/runners/variables.tf Added osx support and license_specifications variable
modules/runners/templates/user-data-osx.sh New macOS bootstrap script template
modules/runners/templates/start-runner-osx.sh New macOS runner startup script
modules/runners/templates/install-runner-osx.sh New macOS runner installation script
modules/runners/templates/install-runner.ps1 Removed trailing newline
modules/runners/scale-down.tf Added osx minimum runtime default of 20 minutes
modules/runners/scale-down-state-diagram.md Updated documentation with OSX timing
modules/runners/main.tf Added osx AMI defaults, templates, and license specification support
modules/runners/README.md Updated documentation with osx references
modules/runner-binaries-syncer/variables.tf Added osx to runner_os validation
modules/runner-binaries-syncer/main.tf Fixed file extension logic to use tar.gz for osx
modules/runner-binaries-syncer/README.md Updated documentation
modules/multi-runner/variables.tf Added license_specifications to runner config
modules/multi-runner/runners.tf Wired license_specifications parameter
modules/multi-runner/README.md Updated documentation
main.tf Wired license_specifications parameter
examples/prebuilt/variables.tf Added osx to description
examples/prebuilt/README.md Updated documentation
examples/dedicated-mac-hosts/* New example for Mac dedicated host provisioning
README.md Updated documentation with osx and license_specifications

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread examples/dedicated-mac-hosts/main.tf Outdated
Comment thread modules/runners/scale-down.tf Outdated
Comment thread modules/runners/scale-down.tf Outdated
@edersonbrilhante edersonbrilhante force-pushed the feat-macos-support branch 3 times, most recently from b7c722c to 019e874 Compare January 30, 2026 16:44

@Brend-Smits Brend-Smits left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @edersonbrilhante

This is a very exciting new feature, great work! Appreciate it a lot.

I've spend quite a bit of time testing this and there are a few things that I noticed:

  • While there is a module now for setting up the dedicated hosts for macos, I wonder if this really makes sense and is the right approach. Not saying this is bad or anything, just wondering. It's quite a challenge now to set up and organize a macos based runner and requires a lot of manual effort still. Also requiring to input the license and host arn can make this less trivial to use than other parts of the framework.
  • I could not get the spawning of instances to work, likely this was caused by the switch on the main branch to using CreateFleet instead of CreateInstance recently. Switching to 'RunInstancesCommand' with a temporary fix like:
async function createInstancesWithRunInstances(
  runnerParameters: Runners.RunnerInputParameters,
  amiIdOverride: string | undefined,
  ec2Client: EC2Client,
): Promise<string[]> {
  const tags = [
    { Key: 'ghr:Application', Value: 'github-action-runner' },
    { Key: 'ghr:created_by', Value: runnerParameters.numberOfRunners === 1 ? 'scale-up-lambda' : 'pool-lambda' },
    { Key: 'ghr:Type', Value: runnerParameters.runnerType },
    { Key: 'ghr:Owner', Value: runnerParameters.runnerOwner },
  ];

  if (runnerParameters.tracingEnabled) {
    const traceId = tracer.getRootXrayTraceId();
    tags.push({ Key: 'ghr:trace_id', Value: traceId! });
  }

  try {
    const instanceType = runnerParameters.ec2instanceCriteria.instanceTypes[0] as _InstanceType;
    const runInstancesCommand = new RunInstancesCommand({
      LaunchTemplate: {
        LaunchTemplateName: runnerParameters.launchTemplateName,
        Version: '$Default',
      },
      InstanceType: instanceType,
      MinCount: runnerParameters.numberOfRunners,
      MaxCount: runnerParameters.numberOfRunners,
      SubnetId: runnerParameters.subnets[0],
      ...(amiIdOverride ? { ImageId: amiIdOverride } : {}),
      TagSpecifications: [
        {
          ResourceType: 'instance',
          Tags: tags,
        },
        {
          ResourceType: 'volume',
          Tags: tags,
        },
      ],
    });

    const result = await ec2Client.send(runInstancesCommand);
    const instanceIds = result.Instances?.map((i) => i.InstanceId!).filter(Boolean) || [];

    if (instanceIds.length === 0) {
      throw new Error('RunInstances returned no instances for dedicated host.');
    }

    return instanceIds;
  } catch (e) {
    logger.warn('RunInstances request failed for dedicated host.', { error: e as Error });
    throw e;
  }
}

resolved the issues I had and resulted in machines getting spawned. So in this case you would have something like:

  // EC2 Fleet (CreateFleet) does not support launching instances onto dedicated hosts
  // for instance types like mac*.metal. Use RunInstances directly instead.
  if (runnerParameters.useDedicatedHost) {
    logger.info('Using RunInstances for dedicated host placement (CreateFleet does not support dedicated hosts).');
    const instances = await createInstancesWithRunInstances(runnerParameters, amiIdOverride, ec2Client);
    logger.info(`Created instance(s) via RunInstances: ${instances.join(',')}`);
    return instances;
  }
  • In addition to the above, I don't think these instance types support spot instance target capacity, meaning it should also not be an option. The example should reflect this as well by using the instance_target_capacity_type: "on-demand" runner option.

I'm sorry for the delay in testing/providing feedback on this PR. It's been a bit of a challenge getting this set up and with things going on outside of work, I've prioritized other things.

@edersonbrilhante

Copy link
Copy Markdown
Contributor Author

@Brend-Smits

While there is a module now for setting up the dedicated hosts for macos, I wonder if this really makes sense and is the right approach. Not saying this is bad or anything, just wondering. It's quite a challenge now to set up and organize a macos based runner and requires a lot of manual effort still. Also requiring to input the license and host arn can make this less trivial to use than other parts of the framework.

That is restriction on AWS side, in a analogy I see mandatory requirement to use macos, like we have for VPC/subnet, but just for macos types.

I could not get the spawning of instances to work, likely this was caused by the switch on the main branch to using CreateFleet instead of CreateInstance recently. Switching to 'RunInstancesCommand' with a temporary fix like:

I will rebase the code and test again.

@edersonbrilhante edersonbrilhante force-pushed the feat-macos-support branch 5 times, most recently from a88f591 to 55ed8d9 Compare March 6, 2026 11:51
@edersonbrilhante

Copy link
Copy Markdown
Contributor Author

@Brend-Smits
I added your code and it worked.

About your concerns:

Host groups require a self-managed license. When using a host group (instead of a specific host), AWS will select one host from the host group.

The “manual” step requires creating a host group, a host, and a license. Then you associate the license with the host group, and associate the host group with the host. This can be done via Terraform and only needs to be done once upfront.

After that, you just configure the module to use the appropriate placement and license_specifications.

This is a caveat when using Mac runners. I think a short doc explaining this should be enough.

@npalm

npalm commented Mar 9, 2026

Copy link
Copy Markdown
Member

@Brend-Smits Thanks for checking. I would suggest we clearly mark in docs as well in variables.

Suggestions:

Comment thread examples/dedicated-mac-hosts/README.md
@Brend-Smits Brend-Smits dismissed npalm’s stale review June 10, 2026 12:46

Feedback was processed, good to merge.

@Brend-Smits Brend-Smits left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM :shipit:!

Nice work!

@Brend-Smits Brend-Smits merged commit 3e179a3 into github-aws-runners:main Jun 10, 2026
42 checks passed
Brend-Smits pushed a commit that referenced this pull request Jun 11, 2026
🤖 I have created a release *beep* *boop*
---


##
[7.7.0](v7.6.1...v7.7.0)
(2026-06-11)


### Features

* Add feature to enable dynamic ec2 config via workflow labels
([#5003](#5003))
([c68445d](c68445d))
* add support for macos runners
([#4930](#4930))
([3e179a3](3e179a3))
* Introduce Amazon Linux 2023 ARM image
([#4780](#4780))
([e572ae5](e572ae5))
* relax cpu_options schema and add amd_sev_snp + nested_virtualization
support
([#5039](#5039))
([5a3746d](5a3746d))
* **runner-role:** Enable using separate IAM role for runners
([#4875](#4875))
([6642e57](6642e57))


### Bug Fixes

* **ci:** sign auto-generated docs commits
([#5154](#5154))
([a6af4d2](a6af4d2))
* **runners:** wire job_retry.lambda_memory_size and lambda_timeout
([#5120](#5120))
([404785e](404785e))
* **scale-up:** Add ec2:TerminateInstances permission to scale-up Lambda
IAM policy
([#5152](#5152))
([94c4e12](94c4e12))
* **scale-up:** prevent negative TotalTargetCapacity when runners exceed
maximum
([#5062](#5062))
([9ab7410](9ab7410))
* **webhook:** Fix publish events to EventBridge
([#5143](#5143))
([a72b737](a72b737))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: runners-releaser[bot] <194412594+runners-releaser[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants