Skip to main content
Azure Pipelines can run macOS jobs on Orka VMs through Microsoft’s self-hosted agent. There’s no Orka plugin or extension for Azure DevOps. You install Microsoft’s agent in an Orka image, register each VM as an agent in a pool, and point your pipelines at that pool, the same way you’d use any self-hosted agent. This page covers the Orka side. For the agent itself, see Microsoft’s Self-hosted macOS agents.

How it works

  • Each Orka VM runs one Azure Pipelines agent, registered in an agent pool in your Azure DevOps organization or collection.
  • The agent connects out to Azure DevOps over HTTPS and asks for jobs. Azure DevOps never connects into your network, so you don’t open any inbound ports.
  • Jobs reach the agents through the pool name in your YAML. Your pipelines, logs, and the Azure DevOps UI don’t change.
  • Agents can stay up across jobs, or take one job each and be deleted afterward. Orka doesn’t watch your Azure DevOps queue, so the single-job model needs your own automation to deploy and delete VMs. See Choose persistent or single-job agents.

Prerequisites

  • An Orka 4.0 cluster with Apple silicon nodes, and the orka CLI. On Orka 3.x, use orka3 in place of orka.
  • An agent pool in Azure DevOps. See Microsoft’s Create and manage agent pools.
  • A personal access token (PAT) with the Agent Pools (read, manage) scope. Azure DevOps uses the PAT only to register an agent, not afterward, so a short-lived PAT works. See Microsoft’s Register an agent using a personal access token.
  • An OCI registry for your agent image, with registry credentials in your Orka namespace. See Manage registry credentials.

Set up Orka for Azure DevOps

1

Build the agent image

Deploy a VM from a base image, then connect to it over SSH. See VM access ports for the SSH port.
To register agents with User Data later, the image needs the Orka 4.0 VM Tools. MacStadium’s latest tags have them; not every version tag does yet.In the VM, install Xcode and anything else your builds need. Then download the macOS ARM64 agent from the agent releases page and extract it, following Microsoft’s Download and configure the agent steps. Use a path without spaces, such as ~/myagent.
Don’t run config.sh in the image, and don’t push a VM whose agent is already registered. A configured agent has its own identity (the .agent and .credentials files), and every VM deployed from the image would share it. Each VM registers itself in a later step.
Push the VM to your registry, and check the push until it finishes:
After the push finishes, delete the build VM with orka vm delete azdo-image-build.
2

Create a VM config

Create a VM config from the agent image, so every agent VM deploys with the same settings:
See VM configuration templates for CPU, memory, and placement options.
3

Deploy and register agents

Deploy a VM for each agent you want. --generate-name gives each VM a unique name:
On each VM, register the agent as the VM’s user, then start it as a service. The commands use the VM’s Orka name as the agent name, read from the VM metadata, and prompt for the PAT so it doesn’t end up in your shell history:
For Azure DevOps Server, use your collection URL for --url. For every config.sh option, see Microsoft’s Unattended config.To register at first boot instead of over SSH, run config.sh from a User Data script. User Data runs as root, and config.sh refuses to run as root by default, so run it as the VM’s user, for example with sudo -u. Start the agent with run.sh in the background (for example, sudo -u <USER> nohup ./run.sh &) instead of svc.sh: the svc.sh service is a LaunchAgent, and at first boot there may be no logged-in session for it to start in. At first boot, the metadata service can take a moment to answer, so retry the curl for the VM’s name until it returns one, for example until AGENT_NAME=$(curl -sf http://169.254.169.254/metadata/orka_vm_name) && [ -n "$AGENT_NAME" ]; do sleep 2; done.Anyone with Kubernetes read access to the VM’s pod or VirtualMachineInstance can read the script, so use a short-lived PAT or have the script fetch it from your secret store. Don’t print the PAT or use set -x: the script’s output goes to /var/log/orka/userdata.log in the VM. See Keep credentials safe.
4

Target the pool from your pipeline

Point your macOS jobs at the pool:
The rest of your pipeline stays the same.

Choose persistent or single-job agents

Persistent agents run as a service and take job after job until you delete the VM. Use them for pipelines that run often and benefit from warm caches. Clean up between jobs in your pipeline, for example with workspace: clean: all, because each job starts where the last one left off. The svc.sh service is a launchd LaunchAgent, which starts only after the VM’s user logs in. If an agent goes offline after its VM restarts, set up automatic login, as Microsoft describes in Automatic login and lock. Single-job agents take one job and exit, so every job starts on a clean VM. Start the agent with ./run.sh --once instead of svc.sh, then delete the VM after the job and deploy a new one. Orka doesn’t do this for you: you need your own automation, using the orka CLI or the Orka API, to keep enough fresh agent VMs registered. Talk to MacStadium if you want help designing it.

Plan capacity

Two limits decide how many jobs run at once:
  • Orka capacity: each node runs at most two macOS VMs, so you can run up to two agents per node.
  • Azure DevOps parallel jobs: Azure DevOps Services limits how many self-hosted jobs run at once by the parallel jobs on your organization. Extra agents beyond that sit idle. Azure DevOps Server doesn’t charge for self-hosted parallel jobs. See Microsoft’s Configure and pay for parallel jobs.

Network access

Agents only need outbound HTTPS from the VMs to Azure DevOps. Microsoft lists the domains to allow in the self-hosted macOS agent FAQ. If your cluster has VM internet isolation turned on, VMs can’t reach Azure DevOps. See VM internet isolation. To build images and deploy VMs, the machine where you run the orka CLI or your automation needs a network path to your Orka cluster: a VPN client for testing, or a site-to-site tunnel for production.

Troubleshooting

  • config.sh prints Must not run with sudo. By default, the agent can’t be configured as root. Run config.sh as the VM’s user.
  • Two VMs show up as one agent, or an agent drops offline when another registers. Agent names must be unique in a pool. Give each VM its own --agent name, for example the VM’s name from the VM metadata, as in Deploy and register agents.
  • An agent goes offline after its VM restarts. The svc.sh service starts only after the VM’s user logs in. See Choose persistent or single-job agents.
  • Jobs stay queued while agents are idle. Check that the YAML names the right pool, that the agents meet the job’s demands, and that you have enough parallel jobs.
For agent issues, see Microsoft’s Troubleshoot pipeline runs.