One runner backs every private-repo CI job in the org
nself-org has exactly one self-hosted runner:
nself-staging-runner status=online busy=true
Every private repo that cannot use free GitHub-hosted minutes (web, plugins-pro, and anything else pinned to self-hosted or to the CI_RUNNER variable) queues behind it. Observed on 2026-08-26: ~90 jobs QUEUED simultaneously, none running, for hours.
Three distinct problems, not one
1. Throughput. plugins-pro alone fans out to ~49 jobs (build/test/typecheck across node 18/20/22, plus Rust). web adds ~60 more. A single runner serialises all of it. A PR that should take minutes takes hours, which in practice means people stop waiting for CI before merging.
2. It manufactures flakes. Concurrent jobs on one box contend for the same host resources. Observed directly on web#126:
Auth E2E — unified auth flows
Error response from daemon: failed to set up container networking:
driver failed programming external connectivity
That is a port already bound by a neighbouring job, not a defect in the code under test. A flake of this shape is worse than a plain failure because it trains people to re-run rather than read.
3. Single point of failure. If this box is down, every private-repo PR is unmergeable — the required status checks can never report. That is the same failure mode as the phantom nself-ci required check found in the 2026-08-26 gate sweep, except triggered by hardware rather than config.
It also runs on the staging box
The name says nself-staging. Per the two-box rule in nsentry-server-standard.md, a CI runner has docker.sock access, which is root-on-host, and should not share a machine with anything holding private data or serving traffic. Worth confirming what else that box does.
Options
- More runners. The cheapest fix for throughput and the SPOF. Even two boxes removes the serialisation cliff and most of the port contention.
- Reduce fan-out.
plugins-pro testing node 18/20/22 for every package is a large matrix; if only one version is actually supported at runtime, the other two are cost with no signal.
- Move what can be moved back to GitHub-hosted. Public repos already use free minutes. Anything private that does not touch secrets could too.
- Isolate ports per job. Does not fix throughput, but stops the container-networking class of flake.
1 and 2 are independent and both help. 4 is worth doing regardless, since the flake will recur on any shared runner.
Constraint to respect
Per the standing org rule, paying GitHub for Actions minutes is not an option and should not be proposed. This is about owned hardware and matrix size, not billing.
Found while landing the org-wide CI gate sweep; it is the last thing blocking plugins-pro#70 and web#126.
One runner backs every private-repo CI job in the org
nself-orghas exactly one self-hosted runner:Every private repo that cannot use free GitHub-hosted minutes (
web,plugins-pro, and anything else pinned toself-hostedor to theCI_RUNNERvariable) queues behind it. Observed on 2026-08-26: ~90 jobs QUEUED simultaneously, none running, for hours.Three distinct problems, not one
1. Throughput.
plugins-proalone fans out to ~49 jobs (build/test/typecheck across node 18/20/22, plus Rust).webadds ~60 more. A single runner serialises all of it. A PR that should take minutes takes hours, which in practice means people stop waiting for CI before merging.2. It manufactures flakes. Concurrent jobs on one box contend for the same host resources. Observed directly on web#126:
That is a port already bound by a neighbouring job, not a defect in the code under test. A flake of this shape is worse than a plain failure because it trains people to re-run rather than read.
3. Single point of failure. If this box is down, every private-repo PR is unmergeable — the required status checks can never report. That is the same failure mode as the phantom
nself-cirequired check found in the 2026-08-26 gate sweep, except triggered by hardware rather than config.It also runs on the staging box
The name says
nself-staging. Per the two-box rule innsentry-server-standard.md, a CI runner hasdocker.sockaccess, which is root-on-host, and should not share a machine with anything holding private data or serving traffic. Worth confirming what else that box does.Options
plugins-protesting node 18/20/22 for every package is a large matrix; if only one version is actually supported at runtime, the other two are cost with no signal.1 and 2 are independent and both help. 4 is worth doing regardless, since the flake will recur on any shared runner.
Constraint to respect
Per the standing org rule, paying GitHub for Actions minutes is not an option and should not be proposed. This is about owned hardware and matrix size, not billing.
Found while landing the org-wide CI gate sweep; it is the last thing blocking
plugins-pro#70andweb#126.