feat(scale-down): opt-in idle confirmation window before terminating not-busy runners - #5228
Open
jensenbox wants to merge 1 commit into
Open
Conversation
…y runners GitHub's runner busy flag can be stale: it reads false for runners that are actively executing a job, both shortly after job assignment (observed 25-60s lag) and deep into a running job (observed 12+ minutes). scale-down trusts a single busy=false reading and terminates mid-job runners (github-aws-runners#5085). When SCALE_DOWN_IDLE_CONFIRMATION_SECONDS > 0 (TF variable scale_down_idle_confirmation_seconds, default 0 = previous behaviour): - on a not-busy reading, tag the instance ghr:idle_detected_at and defer - terminate only when not-busy readings span the confirmation window - any busy reading clears the tag and resets the window - log a per-invocation census of evaluation outcomes Closient C-4227.
jensenbox
added a commit
to closient/terraform-aws-github-runner
that referenced
this pull request
Jul 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Closes the failure mode reported in #5085, including the harder variant discussed in its comments: GitHub's runner
busyflag is not a reliable input for termination decisions. We traced 14 terminated-mid-job runners across one incident window (org-level, non-ephemeral, on-demand, module v7.10.0, scale-down on the default 5-minute schedule) and every one was killed throughremoveRunner's normal path on abusy: falsereading, in two distinct shapes:Variant A — assignment lag. The flag reads
falsefor 25–60+ seconds after a job is already running on the runner:Variant B — stale flag mid-job. The flag flips to
falseon a runner that has been continuously executing one job for many minutes:Another case read
false12m15s into a running job. Because the DELETE call also succeeds in these cases (GitHub's server-side view is consistent with the wrong flag), no re-check-after-deregister sequencing can prevent Variant B — the module needs evidence across time instead of a single reading. With scale-to-zero config (idle_config = []) every instance pastminimum_running_time_in_minutesis evaluated on every tick, so one stale-flag window kills several runners in the same invocation — we measured roughly one multi-runner burst per hour under CI load, each costing a full matrix rerun.Change
Adds an opt-in confirmation window, default off (
scale_down_idle_confirmation_seconds = 0keeps today's single-reading behaviour bit-for-bit):ghr:idle_detected_at=<ISO8601>and defers termination.trueon the tick before their fatal stale reading.evaluated N runner(s): busy=… idle-deferred=… terminated=…) so "ran and found nothing" is distinguishable from "never ran".No new IAM: the scale-down role already carries scoped
ec2:CreateTags/DeleteTagsforghr:orphan. Threaded through the root module andmulti-runner(runner_config.scale_down_idle_confirmation_seconds). Existing behaviours unchanged: bypass-removal, orphan handling, and the "only terminate EC2 after successful de-registration" guard.Cost trade-off: a genuinely idle runner lives one extra evaluation interval before reap (~5 minutes on the default schedule).
Validation
main(542 tests), eslint + prettier clean.300s window, 5-minute schedule) since 2026-07-28 05:32 UTC. Prior baseline: ~1 kill burst/hour. Since deploy: zero mid-job terminations in 14 hours of CI load, ~35 terminations all via the confirmed-idle path, zero de-registration failures. We directly observed the mechanism catching a would-have-been kill: a runner deferred on afalsereading reportedbusy: truefive minutes later and survived.Happy to adjust naming/defaults or split the census logging out if you'd prefer a narrower diff. cc @npwolf / @ben-smyth from #5085 — this covers Variant B from your reports, which the deregister-then-recheck proposal cannot.