Skip to content

feat(runners): boot modes for warm pool instances - #5495

Open
Brend-Smits wants to merge 3 commits into
feat/warm-pool-04-scale-down-sweepfrom
feat/warm-pool-05-boot-hook
Open

Brend-Smits wants to merge 3 commits into
feat/warm-pool-04-scale-down-sweepfrom
feat/warm-pool-05-boot-hook

Conversation

@Brend-Smits

@Brend-Smits Brend-Smits commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Warm pool stack (review in order; each PR is based on the previous one):

  1. feat(compute-providers): add EC2 standby primitives for warm pools #5491 feat(compute-providers): add EC2 standby primitives for warm pools
  2. feat(pool): keep a warm pool of stopped instances #5492 feat(pool): keep a warm pool of stopped instances
  3. feat(scale-up): start warm instances before launching cold runners #5493 feat(scale-up): start warm instances before launching cold runners
  4. feat(scale-down): sweep stopped warm instances #5494 feat(scale-down): sweep stopped warm instances
  5. feat(runners): boot modes for warm pool instances #5495 feat(runners): boot modes for warm pool instances (this PR)
  6. feat(runners): add warm_pool option to the root module #5496 feat(runners): add warm_pool option to the root module
  7. feat(multi-runner): support warm_pool in runner configs #5497 feat(multi-runner): support warm_pool in runner configs
  8. docs(warm-pool): add warm pool guide, ADR and examples #5498 docs(warm-pool): add warm pool guide, ADR and examples
  9. feat(runners): warm pool support for Windows runners #5500 feat(runners): warm pool support for Windows runners
  10. feat(pool): index warm instances, stay within AWS API limits and handle failed starts #5505 feat(pool): index warm instances, stay within AWS API limits and handle failed starts
  11. feat(pool): mark primed warm instances warm from one stop-event queue #5507 feat(pool): mark primed warm instances warm from one stop-event queue

Description

Cloud-init runs user data only on the first boot, but a warm instance boots twice: once to prime and stop, and again when a job is assigned. start-runner.sh now installs a systemd unit that reruns the start logic on later boots and picks a boot mode:

  • RUN: a runner configuration exists in SSM for this instance, so configure and start the runner as before
  • PRIME: the instance is tagged as warm standby and not yet activated, so stop the machine and wait in the pool
  • WAIT: otherwise, poll for the configuration as before

Cleanup no longer terminates a warm instance while it primes. An activated instance logs the time from activation to runner start, and later reboots do not rerun the start logic. The start logic is wrapped in begin/end markers, which the boot hook uses to copy it out of the user data into the systemd script. Behaviour for non-warm runners is unchanged.

EC2 compute provider template

The EC2 compute provider used by the multi-runner-v2 stack keeps its own copy of start-runner.sh. That copy is synced with modules/runners (which also brings over the existing SEGMENT fix in the cleanup trap), so warm instances launched through the v2 stack prime and stop as well. The boot-mode harness takes a template path and fails when the two copies differ.

Test Plan

  • New shell harness modules/runners/templates/tests/start-runner.test.sh with mocked IMDS, SSM and systemd (56 checks), run against both template copies, including the check that they match.
  • terraform test for modules/compute-providers/aws/ec2; the only failure (separates_control_plane_contract_from_ec2_resources) also fails on main.
  • Verified on sandbox instances as part of the end-to-end tests (see the last PR in the stack).

Related Issues

Supersedes #5204.

@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Hook re-extraction, macOS timestamp parsing, and stale tracing context can cause incorrect warm-runner behavior.

Review effort: Balanced
Findings: 1 High severity · 2 Medium severity

Open (3)
What changed in this PR

Adds warm-pool boot modes so Linux runners can prime, stop, and resume for assigned jobs.

Changes:

  • Adds RUN, PRIME, and WAIT boot handling with a systemd hook.
  • Adds shell-based boot-mode tests.
  • Documents custom user-data requirements.
File Description
modules/​runners/​templates/​start-runner.sh Implements boot modes and hook installation.
modules/​runners/​templates/​tests/​start-runner.test.sh Tests boot-mode behavior.
docs/​configuration.md Documents warm-pool boot hooks.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread modules/runners/templates/start-runner.sh Outdated
Comment thread modules/runners/templates/start-runner.sh
Comment thread modules/runners/templates/start-runner.sh
Cloud-init runs user data only on the first boot, but a warm instance
boots twice: once to prime and stop, and again when a job is
assigned. start-runner.sh now installs a systemd unit that reruns the
start logic on later boots and picks a boot mode:

- RUN: a runner configuration exists in SSM for this instance, so
  configure and start the runner as before
- PRIME: the instance is tagged as warm standby and not yet
  activated, so stop the machine and wait in the pool
- WAIT: otherwise, poll for the configuration as before

Cleanup no longer terminates a warm instance while it primes. An
activated instance logs the time from activation to runner start, and
later reboots do not rerun the start logic.

The start logic is wrapped in begin and end markers, which the boot
hook uses to copy it out of the user data into the systemd script.
Adds a shell test harness for the boot modes and documents the
requirement for custom user data templates.

Signed-off-by: Brend Smits <brend.smits@philips.com>
- install_boot_hook extracts the start logic to a temporary file and
  moves it into place. When a parked instance primed again, $0 was the
  hook itself and the redirect truncated it before it was read.
- The BSD date fallback for the activation latency strips both the
  fractional seconds and the trailing Z.
- Warm instances are launched without the pool's ghr:trace_id. Scale-up
  tags its own trace on activation and removes it again on rollback,
  so the runner boot is traced under the scale-up that assigned it.

Signed-off-by: Brend Smits <brend.smits@philips.com>
The EC2 compute provider used by the multi-runner v2 stack keeps its
own copy of start-runner.sh, which did not get the warm pool boot
modes. Warm instances launched through that stack would never prime
and stop, so the pool would evict them as stuck.

Sync the copy with modules/runners (which also brings over the
existing SEGMENT fix in the cleanup trap) and let the boot-mode
harness take a template path and check that both copies match.

Signed-off-by: Brend Smits <brend.smits@philips.com>
@Brend-Smits
Brend-Smits force-pushed the feat/warm-pool-04-scale-down-sweep branch from 6599adf to 2b57f0a Compare October 16, 2026 18:11
@Brend-Smits
Brend-Smits force-pushed the feat/warm-pool-05-boot-hook branch from 73853b7 to 769d4dd Compare October 16, 2026 18:11

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants