Skip to content

feat(api): discover orchestrators via nomad service - #3176

Merged
wj-e2b merged 1 commit into
mainfrom
wj-orchestrator-rollout
Jul 7, 2026
Merged

feat(api): discover orchestrators via nomad service#3176
wj-e2b merged 1 commit into
mainfrom
wj-orchestrator-rollout

Conversation

@wj-e2b

@wj-e2b wj-e2b commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Switch discovery to use nomad's service discovery instead of blindly checking every node in the default nodepool. Lets us use multiple nodepools for cutting over orchestrator from system jobs to service jobs.

Existing orchestrator-ee system job already registers to the service.

@cursor

cursor Bot commented Jul 2, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Wrong or partial discovery would stop the API from reaching orchestrators for sandbox work; the legacy merge reduces rollout ordering risk but both backends must succeed on each sync cycle.

Overview
Nomad orchestrator discovery now uses configured Nomad-native service registrations (address and bound port) instead of treating every ready node in the default pool as an orchestrator on a fixed port, so orchestrators can live in other pools and job layouts. Startup wires service-based discovery with an optional union of the old default-pool node listing for jobs whose registrations still have an empty address, controlled by new environment settings, and tests follow the service API.

Reviewed by Cursor Bugbot for commit 4c073c2. Bugbot is set up for automated code reviews on this repo. Configure here.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

The error check !errors.As(err, &sde) in the deferred wait block will always evaluate to false when a service fails because startService always returns a serviceDoneError regardless of whether the service function f() succeeded or failed. This causes all service failures to be silently ignored, and the orchestrator will incorrectly exit with a success status instead of failing.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/orchestrator/pkg/factories/run.go Outdated
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
3183 2 3181 8
View the top 2 failed test(s) by shortest run time
github.com/e2b-dev/infra/tests/integration/internal/tests/proxies::TestMaskRequestHostAPIParameter
Stack Traces | 4.68s run time
=== RUN   TestMaskRequestHostAPIParameter
=== PAUSE TestMaskRequestHostAPIParameter
=== CONT  TestMaskRequestHostAPIParameter
    mask_request_host_test.go:44: Command [python3] output: event:{start:{pid:1108}}
    mask_request_host_test.go:68: Command [cat] output: event:{start:{pid:1109}}
    mask_request_host_test.go:68: Command [cat] output: event:{data:{stderr:"cat: /tmp/nc_output.txt: No such file or directory\n"}}
    mask_request_host_test.go:68: Command [cat] output: event:{end:{exit_code:1 exited:true status:"exit status 1" error:"exit status 1"}}
    mask_request_host_test.go:69: 
        	Error Trace:	.../tests/proxies/mask_request_host_test.go:69
        	Error:      	Received unexpected error:
        	            	command cat in sandbox iqpkzpcwe8gah1bb0kmkp failed with exit code 1
        	Test:       	TestMaskRequestHostAPIParameter
--- FAIL: TestMaskRequestHostAPIParameter (4.68s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestSandboxCreateWithTag
Stack Traces | 40.5s run time
=== RUN   TestSandboxCreateWithTag
=== PAUSE TestSandboxCreateWithTag
=== CONT  TestSandboxCreateWithTag
    template_tags_test.go:115: Build failed: {<nil> An internal error occurred. Please try again or contact support with the build ID. <nil>}
--- FAIL: TestSandboxCreateWithTag (40.48s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Comment thread packages/api/internal/orchestrator/discovery/nomad.go Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c82c8df518

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/orchestrator/pkg/factories/run.go Outdated
@wj-e2b
wj-e2b force-pushed the wj-orchestrator-rollout branch 2 times, most recently from 46e04d9 to 750ab19 Compare July 6, 2026 18:17
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@wj-e2b
wj-e2b force-pushed the wj-orchestrator-rollout branch from 750ab19 to 37a7881 Compare July 6, 2026 18:17
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@wj-e2b
wj-e2b force-pushed the wj-orchestrator-rollout branch from 37a7881 to fddf68c Compare July 6, 2026 18:23
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Comment thread packages/api/internal/orchestrator/discovery/nomad.go
@wj-e2b wj-e2b changed the title fix(api): discover orchestrators via nomad service feat(api): discover orchestrators via nomad service Jul 6, 2026
…llback

Discover orchestrators by listing registrations of Nomad-native
services (GET /v1/service/<name> per configured name, unioned and
deduped by node; names configurable via
NOMAD_ORCHESTRATOR_SERVICE_NAMES, default "orchestrator") instead of
listing ready Nomad nodes in the hardcoded "default" pool. Service
discovery is node-pool- and job-agnostic, which is required while
orchestrators migrate to the health-gated service job on the dedicated
"orchestrator" pool. ShortID stays the truncated Nomad node ID, so
node identity is stable across the backend switch.

Already-running orchestrators were deployed from a jobspec that
registers the service with an empty Address, so service discovery
alone would miss them until redeployed. To avoid that rollout ordering
constraint, the old node-pool listing is kept as a fallback and
unioned with service discovery: dedup is by ShortID with service
entries winning (they carry the real bound port), and a failure of
either backend fails the whole listing rather than silently degrading
to a subset of nodes. The fallback also keeps draining nodes (still
Status=="ready") discoverable after their services deregister.

The fallback is on by default; set
NOMAD_ORCHESTRATOR_LEGACY_DISCOVERY_ENABLED=false once no legacy jobs
remain.
@wj-e2b
wj-e2b force-pushed the wj-orchestrator-rollout branch from fddf68c to 4c073c2 Compare July 6, 2026 23:16
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Comment thread packages/api/internal/cfg/model.go
@wj-e2b
wj-e2b merged commit 32af250 into main Jul 7, 2026
42 checks passed
@wj-e2b
wj-e2b deleted the wj-orchestrator-rollout branch July 7, 2026 21:19
charlie-e2b pushed a commit that referenced this pull request Jul 30, 2026
🤖 I have created a release *beep* *boop*
---


## 0.0.1 (2026-07-30)


### Features

* add workspace admin API foundations
([#3314](#3314))
([0f72030](0f72030))
* **api:** add admin team API key routes
([#2825](#2825))
([4a1e083](4a1e083))
* **api:** add feature flag to stop accepting E2B access tokens
([#3240](#3240))
([2cf489b](2cf489b))
* **api:** add sandbox fork endpoint
([#3202](#3202))
([643d726](643d726))
* **api:** add sandbox IAM workload token configuration
([13ddb3d](13ddb3d))
* **api:** add sandbox workload identity permission
([#3319](#3319))
([13ddb3d](13ddb3d))
* **api:** add user agent integration attribution to PostHog events
([#3303](#3303))
([d83be18](d83be18))
* **api:** discover orchestrators via nomad service
([#3176](#3176))
([32af250](32af250))
* **api:** e2b access token deprecation feature flag rename
([#3110](#3110))
([ebc2daa](ebc2daa))
* **api:** enforce blocked-team restrictions at mutating API endpoints
([#2659](#2659))
([db848ab](db848ab))
* **api:** filter snapshots by name
([#3184](#3184))
([6fa1bc7](6fa1bc7))
* **api:** gate access token issuance behind feature flag
([#3101](#3101))
([2f7811e](2f7811e))
* **api:** LD-gated ClickHouse read switcher
([#3061](#3061))
([29e74ca](29e74ca))
* **api:** limit template build name to 128 characters
([#3109](#3109))
([84aa186](84aa186))
* **api:** paginated GET /v2/templates (EN-603)
([#3059](#3059))
([91e02e4](91e02e4))
* **api:** per-region volume type defaults from node-derived region
([#3435](#3435))
([baf5559](baf5559))
* **api:** pin resume retries to the node a previous resume timed out on
([#3066](#3066))
([a4fd0f2](a4fd0f2))
* **api:** SOCKS5 egress proxy on sandbox network config (BYOP)
([#2642](#2642))
([1fc3820](1fc3820))
* **api:** soft-delete build layers in DB on user delete
([#3121](#3121))
([ee88776](ee88776))
* **auth:** support admin token team auth
([#2934](#2934))
([5496666](5496666))
* dynamic sandbox log routing and ClickHouse-backed log reads
([#3236](#3236))
([1b19a3b](1b19a3b))
* **evictor:** make max concurrent evictions a feature flag
([#2727](#2727))
([0b33013](0b33013))
* **metrics:** distinguish joined from regular requests (ENG-4072)
([#2699](#2699))
([390e296](390e296))
* **observability:** add kill_reason to sandbox.lifecycle.killed
([#2833](#2833))
([e45418f](e45418f))
* **observability:** include kill_reason in kill-path structured logs
([#2846](#2846))
([33c49f7](33c49f7))
* **orchestrator:** add dummy orchestrator binary for local API dev
([#2744](#2744))
([ab56e25](ab56e25))
* **orchestrator:** report hugepage metrics to API
([#3182](#3182))
([7735bae](7735bae))
* **orchestrator:** track and report last status change timestamp
([#2980](#2980))
([f79be77](f79be77))
* **otel:** instrument auth service HTTP client with otelhttp
([#2722](#2722))
([69b085d](69b085d))
* per-team events TTL limit (tier + addons)
([#3181](#3181))
([f76b2cb](f76b2cb))
* **storage:** stamp provenance custom metadata on uploaded objects
(incl. headers) ([#3033](#3033))
([ba8604e](ba8604e))


### Bug Fixes

* added api and orch
([#3454](#3454))
([fda5e45](fda5e45))
* **api:** check template alias tags in exists endpoint
([#2916](#2916))
([9574cdf](9574cdf))
* **api:** copy auth/internal into api and dashboard-api image builds
([#3323](#3323))
([bda1fee](bda1fee))
* **api:** discover the local orchestrator as a template builder
([#3386](#3386))
([9ea005a](9ea005a))
* **api:** expose pagination headers via CORS
([#3388](#3388))
([e832b1e](e832b1e))
* **api:** handle corrupted data in sandbox stop time
([#3203](#3203))
([a98a178](a98a178))
* **api:** include exhaustion reason in "Node exhausted" placement
warning ([#3279](#3279))
([eb3797b](eb3797b))
* **api:** invalidate auth cache on API key deletion
([#3324](#3324))
([8b02910](8b02910))
* **api:** keep API alive until in-flight requests finish
([#2708](#2708))
([06378c7](06378c7))
* **api:** let the analytics collector address carry a port
([#3394](#3394))
([6d41cb5](6d41cb5))
* **api:** parse the pause body regardless of Content-Length
([#3056](#3056))
([d66aab8](d66aab8))
* **api:** prevent uint64 underflow in node allocated metrics
([#3216](#3216))
([fed38e1](fed38e1))
* **api:** push api and db-migrator images to both latest and commit S…
([#2951](#2951))
([6f010fc](6f010fc))
* **api:** reject non-positive timeout on sandbox create, resume, and
fork ([#3419](#3419))
([b672bd1](b672bd1))
* **api:** report invalid tag errors as bad requests
([#2799](#2799))
([10085a1](10085a1))
* **api:** stop evicting the local node during sync
([#2881](#2881))
([5455905](5455905))
* **api:** use correct error variable in processCustomErrors
([#3135](#3135))
([a131a00](a131a00))
* **auth:** rename X-Team-Id header to X-Team-ID
([#2723](#2723))
([f92ecc0](f92ecc0))
* correct 3 CVES ([#3218](#3218))
([076823b](076823b))
* **orchestrator:** reject standby while draining
([#3325](#3325))
([475a7ee](475a7ee))
* Support snapshots for non-default clusters
([#2947](#2947))
([28eeb72](28eeb72))


### Performance Improvements

* **api:** wake reservation waiters via pub/sub instead of 20ms polling
[ENG-4070] ([#2729](#2729))
([2944d06](2944d06))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com>
jakubno pushed a commit that referenced this pull request Aug 3, 2026
🤖 I have created a release *beep* *boop*
---


## 0.0.1 (2026-07-30)


### Features

* add workspace admin API foundations
([#3314](#3314))
([0f72030](0f72030))
* **api:** add admin team API key routes
([#2825](#2825))
([4a1e083](4a1e083))
* **api:** add feature flag to stop accepting E2B access tokens
([#3240](#3240))
([2cf489b](2cf489b))
* **api:** add sandbox fork endpoint
([#3202](#3202))
([643d726](643d726))
* **api:** add sandbox IAM workload token configuration
([13ddb3d](13ddb3d))
* **api:** add sandbox workload identity permission
([#3319](#3319))
([13ddb3d](13ddb3d))
* **api:** add user agent integration attribution to PostHog events
([#3303](#3303))
([d83be18](d83be18))
* **api:** discover orchestrators via nomad service
([#3176](#3176))
([32af250](32af250))
* **api:** e2b access token deprecation feature flag rename
([#3110](#3110))
([ebc2daa](ebc2daa))
* **api:** enforce blocked-team restrictions at mutating API endpoints
([#2659](#2659))
([db848ab](db848ab))
* **api:** filter snapshots by name
([#3184](#3184))
([6fa1bc7](6fa1bc7))
* **api:** gate access token issuance behind feature flag
([#3101](#3101))
([2f7811e](2f7811e))
* **api:** LD-gated ClickHouse read switcher
([#3061](#3061))
([29e74ca](29e74ca))
* **api:** limit template build name to 128 characters
([#3109](#3109))
([84aa186](84aa186))
* **api:** paginated GET /v2/templates (EN-603)
([#3059](#3059))
([91e02e4](91e02e4))
* **api:** per-region volume type defaults from node-derived region
([#3435](#3435))
([1bded44](1bded44))
* **api:** pin resume retries to the node a previous resume timed out on
([#3066](#3066))
([a4fd0f2](a4fd0f2))
* **api:** SOCKS5 egress proxy on sandbox network config (BYOP)
([#2642](#2642))
([1fc3820](1fc3820))
* **api:** soft-delete build layers in DB on user delete
([#3121](#3121))
([ee88776](ee88776))
* **auth:** support admin token team auth
([#2934](#2934))
([5496666](5496666))
* dynamic sandbox log routing and ClickHouse-backed log reads
([#3236](#3236))
([1b19a3b](1b19a3b))
* **evictor:** make max concurrent evictions a feature flag
([#2727](#2727))
([0b33013](0b33013))
* **metrics:** distinguish joined from regular requests (ENG-4072)
([#2699](#2699))
([390e296](390e296))
* **observability:** add kill_reason to sandbox.lifecycle.killed
([#2833](#2833))
([e45418f](e45418f))
* **observability:** include kill_reason in kill-path structured logs
([#2846](#2846))
([33c49f7](33c49f7))
* **orchestrator:** add dummy orchestrator binary for local API dev
([#2744](#2744))
([ab56e25](ab56e25))
* **orchestrator:** report hugepage metrics to API
([#3182](#3182))
([7735bae](7735bae))
* **orchestrator:** track and report last status change timestamp
([#2980](#2980))
([f79be77](f79be77))
* **otel:** instrument auth service HTTP client with otelhttp
([#2722](#2722))
([69b085d](69b085d))
* per-team events TTL limit (tier + addons)
([#3181](#3181))
([f76b2cb](f76b2cb))
* **storage:** stamp provenance custom metadata on uploaded objects
(incl. headers) ([#3033](#3033))
([ba8604e](ba8604e))


### Bug Fixes

* added api and orch
([#3454](#3454))
([d56e0a8](d56e0a8))
* **api:** check template alias tags in exists endpoint
([#2916](#2916))
([9574cdf](9574cdf))
* **api:** copy auth/internal into api and dashboard-api image builds
([#3323](#3323))
([bda1fee](bda1fee))
* **api:** discover the local orchestrator as a template builder
([#3386](#3386))
([9ea005a](9ea005a))
* **api:** expose pagination headers via CORS
([#3388](#3388))
([e832b1e](e832b1e))
* **api:** handle corrupted data in sandbox stop time
([#3203](#3203))
([a98a178](a98a178))
* **api:** include exhaustion reason in "Node exhausted" placement
warning ([#3279](#3279))
([eb3797b](eb3797b))
* **api:** invalidate auth cache on API key deletion
([#3324](#3324))
([8b02910](8b02910))
* **api:** keep API alive until in-flight requests finish
([#2708](#2708))
([06378c7](06378c7))
* **api:** let the analytics collector address carry a port
([#3394](#3394))
([6d41cb5](6d41cb5))
* **api:** parse the pause body regardless of Content-Length
([#3056](#3056))
([d66aab8](d66aab8))
* **api:** prevent uint64 underflow in node allocated metrics
([#3216](#3216))
([fed38e1](fed38e1))
* **api:** push api and db-migrator images to both latest and commit S…
([#2951](#2951))
([6f010fc](6f010fc))
* **api:** reject non-positive timeout on sandbox create, resume, and
fork ([#3419](#3419))
([5a4b631](5a4b631))
* **api:** report invalid tag errors as bad requests
([#2799](#2799))
([10085a1](10085a1))
* **api:** stop evicting the local node during sync
([#2881](#2881))
([5455905](5455905))
* **api:** use correct error variable in processCustomErrors
([#3135](#3135))
([a131a00](a131a00))
* **auth:** rename X-Team-Id header to X-Team-ID
([#2723](#2723))
([f92ecc0](f92ecc0))
* correct 3 CVES ([#3218](#3218))
([076823b](076823b))
* **orchestrator:** reject standby while draining
([#3325](#3325))
([475a7ee](475a7ee))
* Support snapshots for non-default clusters
([#2947](#2947))
([28eeb72](28eeb72))


### Performance Improvements

* **api:** wake reservation waiters via pub/sub instead of 20ms polling
[ENG-4070] ([#2729](#2729))
([2944d06](2944d06))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants