Migrate from Actions Runner Controller (ARC) to Managed Runners
ARC works, but you maintain a Kubernetes cluster, the controller, and scaling config to get ephemeral runners. Managed runners give you ephemeral runners with none of that.
Actions Runner Controller (ARC) runs self-hosted runners as pods and scales them with runner scale sets. It is real infrastructure: a cluster, the controller, listeners, and upgrade cycles. Managed runners replace the whole stack with a runs-on: label.
What changes
| ARC | Managed (Latchkey) |
|---|---|
runs-on: arc-runner-set | runs-on: latchkey-medium |
| You run a Kubernetes cluster | No cluster |
| You manage the controller + scale sets | Warm pools + autoscale built in |
| You handle controller/runner upgrades | Images maintained for you |
Before and after
jobs:
build:
runs-on: arc-runner-set # ARC runner scale set
steps:
- uses: actions/checkout@v4
- run: npm ci && npm testManaged equivalent
jobs:
build:
runs-on: latchkey-medium # no cluster to run
steps:
- uses: actions/checkout@v4
- run: npm ci && npm testMigration steps
- Inventory which workflows target your ARC runner scale set labels.
- Connect the managed-runner provider to your org.
- Swap the ARC label for the managed label on one workflow.
- Verify parity, roll out, then scale the ARC deployment to zero.
- Tear down the controller and (if dedicated) the cluster.
Gotchas
- If jobs relied on in-cluster services or node-local mounts, replace those with
services:containers or steps. - Container-mode ARC jobs may need
container:on GitHub-managed runners. - Keep ARC scaled down (not deleted) until you have run in parallel and trust the new path.
What you gain
- Roughly 70% lower per-minute cost than GitHub-hosted runners.
- Warm pools remove queue time - jobs start almost immediately.
- Self-healing detects, fixes, and auto-retries transient and mechanical failures.
- Zero ops: no agents, autoscalers, or controllers to patch and babysit.
Key takeaways
- ARC trades GitHub-hosted cost for cluster + controller upkeep.
- Managed runners give ephemeral runners with no cluster.
- Migration is a label swap, then scale ARC to zero.