Runners
A runner is whatever executes a job. Two kinds are yours to use: the hosted
pool, which is the default and needs no setup, and self-hosted runners — the
neo CLI acting as an agent on your own machine, either standing by for work
(neo runner start) or for one push at a time (neo push --runner).
Hosted runners
Section titled “Hosted runners”Hosted runners are neosource’s own pool, and they are what runs your jobs. You do not configure or provision anything: push a workflow and jobs are scheduled onto the pool.
Every job runs in the same canonical environment, with tools added by
packages:. See The job environment — that page is the
one that determines what your job can actually do.
What runs-on selects
Section titled “What runs-on selects”This is the most common misunderstanding coming from GitHub Actions.
runs-on is a scheduling label. It does not choose an environment. There is
no runs-on value that gives your job a different base image or a different
operating system.
jobs: a: runs-on: ubuntu-latest # a label packages: [nodejs_20] # this is what determines the environment
b: runs-on: nix # requests the strict Nix form packages: [nodejs_20] # same tools either wayruns-on: nix is now a scheduling alias like any other label. packages: and
flake: are job-level keys honoured on every runs-on; the only rule is that
a job may not declare both.
A job that declares no packages: and no flake: runs in the canonical
container image. A job that declares either runs under Nix. That decision comes
entirely from the job’s own keys, never from its runs-on label.
Self-hosted runners
Section titled “Self-hosted runners”You can attach your own hardware to the pool. Registration, job claim by
long-poll, lease heartbeat and completion all work, driven by an agent that is
embedded in the neo CLI — there is no second binary to install:
neo runner startZero-arg inside a pushed checkout with a stored login. start registers first if
this machine has no persisted credentials yet, so it is the only command most
people need; neo runner register exists for scripts that want registration as
its own step. Credentials persist to ~/.config/neosource/runner.json. Get the
CLI from Install the CLI.
Two flags decide what the machine will accept:
--labels(defaultself-hosted,linux,x64) is what the runner advertises forruns-onmatching.--serve-default-labelsalso advertises the hosted labels (ubuntu-latest,nix) so unmodified workflows can land here.- Registration is actor-bound by default: it claims only jobs from runs you
triggered — your pushes, your PRs, your dispatches.
--workspace-wideclaims any job in the workspace and requires workspace admin, which the server enforces.
Two operational rules worth knowing:
- A runner registration is swept after 24 hours without claiming a job, so a
machine left off for a day comes back to a dead credential.
neo runner starthandles this for you, whether it was running the whole time or has just been restarted: when the scheduler stops recognising the credential it drops the claim loop, registers again, and resumes. You do not have to re-runneo runner registerby hand. The one limit is a bounded retry budget — five revocations in quick succession and the agent gives up and exits non-zero rather than churning against a scheduler that keeps rejecting it. - The hosted pool stays the fallback. A self-hosted machine that dies mid-run does not strand the job — the lease expires and the job is requeued.
Local-first CI
Section titled “Local-first CI”Running your own push’s CI on your own machine while the server still records the run — real run records, real logs, visible in the UI, just executed locally:
neo push --runnerAn ephemeral actor-bound session runner registers before the push, claims that push’s jobs, streams their logs into your terminal, and exits when the runs finish. The hosted pool is the fallback if this machine dies mid-run.
Seeing what ran
Section titled “Seeing what ran”Runs, jobs, and logs are all readable through the API and the Actions view in the web UI. The endpoint list is in How CI works here.