How to run a Buildkite agent on a Mac
To run Buildkite jobs on a Mac, install the agent from Buildkite's Homebrew tap. Paste an agent token into its config file and set a queue tag. Start it with brew services, so it runs as a LaunchAgent, and turn on automatic login. Then target the queue from your pipeline steps.
What you need first
- A Buildkite cluster, and permission to manage its agent tokens and queues. You need to be an org admin or a cluster maintainer.
- A Mac on macOS 11 or newer. That is Buildkite's stated minimum. Apple silicon, Homebrew, Xcode and admin access.
- An SSH key the agent can use to clone your repositories.
1. Create a queue and an agent token
In Buildkite, select Agents to reach the Clusters page and pick your cluster. On the Queues page, select New Queue. Give it the key macos and choose Self hosted. Then open Agent Tokens, select New Token, add a description, and create it. Copy the value. Buildkite shows it once.
The token form has an Allowed IP Addresses field. Leave it empty on MacRun. Our Macs have no static public IP, so a CIDR rule would lock the agent out.
2. Install the agent with Homebrew
brew tap buildkite/buildkite brew trust buildkite/buildkite brew install buildkite/buildkite/buildkite-agent
Homebrew 7.0.7, the version on our Mac in October 2026, refuses formulas from a third-party tap until you trust it. That is the middle line. The formula is now named buildkite-agent@3, and the old name in Buildkite's docs still points to it.
On Apple silicon the files land under /opt/homebrew:
- Config:
/opt/homebrew/etc/buildkite-agent/buildkite-agent.cfg - Hooks:
/opt/homebrew/etc/buildkite-agent/hooks - Log:
/opt/homebrew/var/log/buildkite-agent.log
Run brew info buildkite-agent to see the exact paths on your machine.
3. Add the token and the queue tag
Buildkite's docs use sed to swap the placeholder token. Replace the text in capitals with your token.
sed -i '' "s/xxx/INSERT-YOUR-AGENT-TOKEN-HERE/g" "$(brew --prefix)"/etc/buildkite-agent/buildkite-agent.cfg cat "$(brew --prefix)"/etc/buildkite-agent/buildkite-agent.cfg | grep token
Then open the same file and set the tags line, so the agent joins your queue:
tags="queue=macos"
An agent belongs to one self-hosted queue in a cluster. With no queue tag, it joins the default queue. If the cluster has no default self-hosted queue, Buildkite says the agent fails to connect.
4. Test it, then run it as a service
Start it once in the foreground. It should show up on the cluster's agent list.
buildkite-agent start
Stop it with Control C. The Homebrew formula ships a service definition. It runs buildkite-agent start with the config above, restarts on failure, and logs to the same file. Start it from Terminal on the Mac's desktop:
brew services start buildkite/buildkite/buildkite-agent@3 brew services list | grep buildkite
On macOS, the agent runs as the user who started the launchd service. Start it as the account that owns Xcode and your signing keys.
Start with one agent per Mac. Buildkite documents a spawn setting in the config file, and a --spawn flag, to run several agents from one service. Two Xcode builds at once compete for the same cores and memory. On a 16 GB machine, measure a single agent first, then try two.
5. Make it survive a reboot
The formula's own install notes say to set the Mac to log in automatically as this user. Buildkite's tap README explains the trade. A LaunchAgent needs a login, but it lets tests use GUI tools such as the iOS Simulator. Turn on automatic login in System Settings, under Users and Groups. Then turn off sleep, reboot, and check the agent list.
sudo pmset -a sleep 0 sudo shutdown -r now
Keep the plist in your home folder's LaunchAgents, which is where Homebrew puts it. On macOS 27 we saw a plist in /Library/LaunchAgents break auto-login. See auto-login on a headless Mac.
6. A pipeline step for the Mac
steps:
- label: ":xcode: iOS tests"
agents:
queue: "macos"
commands:
- "rm -rf build/TestResults.xcresult"
- "xcodebuild test -project MyApp.xcodeproj -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.5' -resultBundlePath build/TestResults.xcresult"
- "ditto -c -k --keepParent build/TestResults.xcresult build/TestResults.xcresult.zip"
artifact_paths:
- "build/TestResults.xcresult.zip"
timeout_in_minutes: 30
retry:
automatic:
- exit_status: -1
limit: 2The exit_status: -1 retry comes from Buildkite's command step example. It retries a job when the agent itself was lost, not when a test failed.
Common errors and fixes
launchctlsays Could not find domain for. Buildkite says a user must be logged in to the Mac. Log in on the desktop and load the service again.Refusing to load formula ... from untrusted tap. Runbrew trust buildkite/buildkiteand install again.- The agent will not connect. The token is wrong, or the queue key in tags does not exist in this cluster.
- The agent cannot clone. Put the key in
~/.sshof the user that runs the agent. xcodebuild: error: Existing file at -resultBundlePath. Builds reuse the checkout. Delete the bundle first, as above.
To upgrade later, run brew update && brew upgrade buildkite/buildkite/buildkite-agent@3.
Why a dedicated Mac helps
Buildkite is built for your own machines, and a persistent Mac keeps its caches. In our benchmark on the Wikipedia iOS app with Xcode 26.6, a job after a small change took 27 seconds on a warm M6. A fresh GitHub-hosted macos-26 runner took 269 seconds.
When Buildkite hosted agents are enough
Buildkite also offers hosted macOS agents. You pick them when you create a hosted queue, per its docs read in October 2026. If you want no machine to look after, that is the simpler path. MacRun is also the wrong pick if you need an SLA, a static IP for token rules, or more than one region.
For the MacRun side, see other CI systems. For what to run in the steps, see the iOS CI/CD pipeline guide.
Frequently asked questions
Where is the Buildkite agent config file on a Mac?
+
With Homebrew on Apple silicon it is /opt/homebrew/etc/buildkite-agent/buildkite-agent.cfg. Run brew info buildkite-agent to confirm the path.
Which user does the Buildkite agent run as on macOS?
+
The user who started the launchd service. Start it as the account that owns Xcode and your signing keys.
Why use a LaunchAgent and not a LaunchDaemon for Buildkite?
+
Buildkite's tap README says a LaunchAgent needs a login but lets tests use GUI tools such as the iOS Simulator. Pair it with automatic login.
How do I send a step to my Mac agent?
+
Give the agent a tag like queue=macos and add agents: queue: macos to the step.