PlatformRunner machines

Runner machines

Windows, macOS and self-hosted jobs, on machines you register.

Some jobs cannot run on the cluster. A Windows container needs the NT kernel and cannot exist on a Linux host at all, and macOS does not containerise — so for windows-latest and macos-latest there is no pod anywhere. Register the machine instead, and the platform reaches it over SSH.

app.equantic.space/settings?section=runner-hosts
windows-latestmacos-latest
win-runner-1windows/amd64runner@10.0.0.42Accepting jobs
mac-mini-1macos/arm64runner@10.0.0.51Accepting jobs
build-boxlinux/amd64runner@10.0.0.7Draining
mac-studiomacos/arm64runner@10.0.0.52unreachable
1The labels a job asks for
2Open a session and ask
3The public key to install
Each machine shows what the platform actually knows about it: the labels it answers to, and whether it has ever been reached.

When you need one

runs-onWhere it runs
ubuntu-latest, a container imageThe cluster. Nothing to register.
windows-latestA registered Windows machine.
macos-latestA registered Mac. Apple's licence requires Apple hardware.
self-hostedA registered machine of any OS — that is what the label means.

A job whose labels match no machine fails at once rather than running somewhere wrong, and its annotation says what is missing: no machine for that OS, a machine that is draining, or a label none has. The run fails rather than reporting success for work that was never built.

Registering a machine

Go to Settings → Platform → Runner machines and press Register machine. You declare what the machine is — address, the account CI will run as, its OS and architecture, and how many jobs it may take at once.

You are never asked for a private key. The platform generates an ed25519 pair, encrypts the private half at rest with the same cipher the secrets use, and shows you only the public half.

That is deliberate. Pasting a private key into a browser form means moving the most dangerous string you own through a page, a request and a clipboard — and the key most people reach for is their own login key, which grants a great deal more than running CI.

Installing the key

The screen gives you the command for that machine's OS, because the destination genuinely differs.

On Linux and macOS, the usual place:

bash
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo 'ssh-ed25519 AAAA… eqs-mac-mini-1' >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

On Windows, not that place. For any account in the Administrators group, OpenSSH reads one machine-wide file and ignores ~/.ssh/authorized_keys entirely:

powershell
Add-Content -Path "$env:ProgramData\ssh\administrators_authorized_keys" -Value 'ssh-ed25519 AAAA… eqs-win-runner-1'
icacls "$env:ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"
Installing to the home directory on Windows succeeds, changes nothing, and the next connection is refused with Permission denied (publickey) — a failure that reads like a bad key. Also make sure the file ends in a newline: a key appended to an unterminated last line joins the two into one invalid entry, and then nothing authenticates at all.

Testing the connection

Test opens a session and asks the machine what it is. One round trip answers three questions: SSH works, a POSIX shell is there, and which machine this actually is.

A machine registered under the wrong OS fails the check and says so. That is not a connection problem and it is worse than one: the router would send windows-latest jobs to a Linux box, and each would fail on a command that is not there, with nothing pointing back at the registration.

When a check fails you get the reason verbatim — ssh: connect to host 10.0.0.42 port 22: Operation timed out — rather than a generic error. A machine that has never been reached says so, which is a different thing from unreachable.

What the machine needs

  • An SSH server, and an unprivileged account for CI to run as.
  • A POSIX shell. On Windows that means Git for Windows, with its bash.exe set as the OpenSSH DefaultShell — every step runs through it.
  • git and tar, both of which Git for Windows ships.

Nothing else. The platform places the Node runtime that JavaScript actions need — built for that machine's own OS and architecture — the first time a job runs there.

Draining and removing

Drain finishes the jobs in flight and takes no new ones: the state to use before rebooting or updating a machine. Remove makes the platform forget it.

Removing does not touch the machine. Its authorized_keys still carries the platform's public key until you take it out.

Isolation is weaker than a container

Worth saying plainly. A job on a registered machine sees whatever the previous job left outside its workspace: the home directory, /tmp, installed packages, the state of any daemon. A container gives a fresh root each time and this does not.

What is mitigated: the account is unprivileged, the workspace is per job, and cleanup refuses any path outside the work root. Use a registered machine for work you would be comfortable running on a shared build box.