FR
live

Self-hosted GitHub Actions runners stop receiving jobs on September 25

GitHub has tightened minimum-version enforcement for self-hosted runners: brownouts began September 14, and from September 25, 2026 any runner not updated within 30 days stops receiving jobs. The failure is silent — jobs sit in “Queued” with no error: audit your runners before the next window.

A row of dark rack-mount servers, one amber connector hanging unplugged from its chassis.

September 14, 2026. The brownouts begin: outdated runners intermittently fail to register. September 16 and 18. The next two brownout windows. September 25, 2026. Hard enforcement: no job is queued to a self-hosted runner that has not been updated within 30 days. Why it matters: the failure is silent — no error in your workflows, just jobs sitting in “Queued” indefinitely.

What GitHub changed under the hood

GitHub rebuilt the Actions backend from scratch. The old platform handled roughly 23 million jobs per day; the new one absorbs 120 million or more — over five times the volume — and lets enterprises start seven times more jobs per minute. Old runner versions cannot talk to the new infrastructure: this is a technical incompatibility, not a policy choice.

As a result, GitHub set a minimum version (2.329.0) and a rolling rule: every runner must be updated within 30 days of each new release or it stops receiving jobs. Enforcement has been pushed twice already — from December 2025 to March 2026, then to September 2026. There are no more extensions on the table.

Who is actually at risk

Most runners auto-update by default. If you have not explicitly disabled updates, there is a good chance you are already fine. The real danger is runners that opted out of automatic updates:

  • Runners launched with --disableupdate — common in Docker images, Helm charts and Terraform modules written months ago. The flag is often buried and forgotten.
  • Container-based runners (Kubernetes/ARC) — the runnerVersion field in a RunnerDeployment or AutoscalingRunnerSet spec must be bumped explicitly.
  • Air-gapped runners — they cannot auto-download updates; every release requires a manual process.
  • Orphaned runners — set up by someone who has since left the team, pinned to an old version, never revisited.

The --disableupdate flag was a reasonable choice for teams that wanted control over their build environment. It now creates mandatory maintenance overhead: you must update by hand within 30 days of each release. For most teams, that is a worse tradeoff than auto-updates.

How to audit before the 25th

The September 3 changelog shipped a dedicated REST API, available at the repository, organization and enterprise levels:

bash
# Exact deprecation dates for a given runner version
curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
  "https://api.github.com/orgs/ACME/actions/runners/deprecations/2.323.0"

The response returns runtime_deprecates_at and registration_deprecates_at — a runner can stop accepting new registrations before it stops executing. To list every runner in an organization with its version:

bash
gh api "/orgs/ACME/actions/runners" \
  --jq '.runners[] | {name: .name, version: .version}'

You can also check via Settings → Actions → Runners in the UI: click any runner to see its version.

How to fix it

If your runners auto-update, they should already be current: verify and move on. If you have runners with --disableupdate:

bash
# 1. Stop the runner service
sudo ./svc.sh stop

# 2. Replace the binary with the latest release
curl -o runner.tar.gz -L https://github.com/actions/runner/releases/latest/download/actions-runner-linux-x64-2.329.0.tar.gz
tar xzf runner.tar.gz
./config.sh --url https://github.com/ACME/REPO --token "$RUNNER_TOKEN"
sudo ./svc.sh start

For actions-runner-controller (ARC): set runnerVersion: latest in your AutoscalingRunnerSet spec, or pin a version above 2.329.0, then redeploy.

The remaining brownout schedule — September 16, September 18, then September 25 in hard enforcement — leaves eleven days. The brownouts have already started: check your runners before the next window hits.

What the shift says about your CI dependency

Beneath the procedural surface, this episode is a reminder that CI/CD is production infrastructure like any other. A self-hosted runner is a node that executes arbitrary code with your secrets — KUBECONFIG, AWS keys, registry tokens. The fact that an outdated runner can fail silently — jobs stuck in “Queued” with no error — turns simple version neglect into a stealth outage, discovered hours after the commit.

It is also a lesson in the debt of self-hosted builds. Those who chose --disableupdate for stability now inherit a manual burden; those who leave auto-update on accept that a version change can land at any moment mid-run. There is no universally right answer — there is a choice to own and instrument: either automate updates with a controlled window, or wire the deprecation API into your monitoring to schedule upgrades ahead of the deadline.

Detecting the silent failure

The insidious part of this deadline is its failure mode. A runner that is no longer eligible produces no error: the workflow still triggers, but the job piles up in “Queued” without ever starting. A team that does not actively watch its queue length will discover the outage when someone asks why the release never shipped — not when it happened.

Two simple signals are worth wiring up before September 25. The first is time-in-“Queued: an alert that fires when a job waits more than a few minutes is a generic detector of an unhealthy build infrastructure, runner deadline included. The second is the deprecation API itself: a daily job that calls GET /actions/runners/deprecations/{version} for each deployed version and compares runtime_deprecates_at against today turns the upgrade into a planned operation instead of a race.

The silent failure has a second, less visible consequence: it erodes trust in the green status. If a pipeline can be “green” with jobs that never actually ran, green stops being proof. For teams that attach compliance guarantees to a CI pass — signed build, dependency scan, attested deploy — a phantom runner is a hole in the chain of evidence, not just an operational nuisance.

The ARC and air-gapped exceptions

Two populations deserve a closer look before the deadline. actions-runner-controller (ARC) users manage runners through Kubernetes specs rather than a binary on a VM, so the upgrade path is not “run a script” but “edit a RunnerDeployment or AutoscalingRunnerSet and redeploy.” The runnerVersion field defaults to a pinned value; setting runnerVersion: latest removes the manual bump but means a new release can roll through your cluster at any moment. Pinning above 2.329.0 with a scheduled review cadence is the middle ground for teams that want both control and compliance.

Air-gapped runners are the harder case. They cannot reach the update service, so every new release requires downloading the runner package on a connected machine, transferring it into the network, and re-running config.sh with a fresh registration token. The 30-day rolling rule turns this into a standing operational task. If you run air-gapped builds, the deadline is not September 25 — it is a recurring 30-day clock you now own, and the only durable answer is to automate the transfer and configuration steps or move the build to a managed runner pool.

GitHub Enterprise Server (on-premises) is explicitly out of scope for this enforcement, and GitHub Enterprise Cloud with Data Residency already hit full enforcement on July 31 — so if you run the latter and nothing broke, your fleet was already compliant.

Verdict

If you run self-hosted runners, assume nothing: audit versions now via the deprecation API or the UI, and treat any runner below 2.329.0 or not updated within 30 days as a scheduled incident. If you use ARC, bump runnerVersion before September 25. And if you host air-gapped or orphaned builds, this is the moment to question that architecture: a build infrastructure that requires manual intervention on every release is an outage waiting to happen, not a control choice.

References

The cyber brief, every Tuesday

The flaws that matter and the patches to apply, in a ten-minute read.

No spam. One-click unsubscribe.
read next

On the same topic

Memory QoS graduates to beta and ships enabled by default in Kubernetes 1.37

Kubernetes Memory QoS, which guides the Linux kernel on container memory handling through cgroup v2, graduates to beta and turns on by default in version 1.37. The more important change lies elsewhere: the implicit throttling factor disappears, making the upgrade a no-surprise event for existing clusters.

Ingress-nginx is end of life: migrate to Gateway API before November 2026

On November 12, 2025, Kubernetes announced the retirement of ingress-nginx; upstream maintenance stopped in March 2026 and the last patch bridge, Microsoft’s for AKS, expires in November 2026. Every annotation in your fleet now has to become a Gateway API route — or remain an unpatched vulnerability.

← Back to the feed

Type at least two characters.

navigate open esc dismiss