Skip to content

docs(sandbox): OpenSandbox best-practices and debugging guides - #3062

Draft
hemildesai wants to merge 16 commits into
mainfrom
hemild/opensandbox-best-practices
Draft

docs(sandbox): OpenSandbox best-practices and debugging guides#3062
hemildesai wants to merge 16 commits into
mainfrom
hemild/opensandbox-best-practices

Conversation

@hemildesai

@hemildesai hemildesai commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Two new docs pages under infrastructure/sandbox/, distilled from several weeks of running thousands of concurrent OpenSandbox sandboxes for SWE-bench / Terminal-Bench / DeepSWE evals. Every recommendation traces to a real incident or a measured A/B.

opensandbox-best-practices

Opens with an eight-item short list (each linking to its section), then in that order:

  • Connection fields that must be rightuse_server_proxy: true (direct mode dials pod IPs; unreachable from outside the cluster), domain via OPENSANDBOX_DOMAIN, transport_backend: aiohttp, max_connections: null, keepalive_expiry_s, protocol.
  • Requests are not limitsresources becomes the pod limits; requests live under provider_options.resource_requests; setting only resources applies it as both, so a 4 CPU / 32Gi run occupies ~30× the capacity of a 0.5 / 512Mi one and pushes everyone's creates into Pending.
  • Host-core fan-out — framed as cluster-dependent (host cores vs LXCFS-overlaid limits, how to check), what derive_cpu_env covers and cannot, why group-OOM kills the whole sandbox, bash timeout as the recovery mechanism.
  • Background execution and timeouts — how submit/poll/fetch works, what each poll knob controls (with request-rate math), create.timeout_s vs ready_timeout_s, ttl_s, and the "failures every N minutes" create-timeout echo.
  • Retriescreate.retries and operations.retries (idempotent / provably-never-started ops) vs command_retries: 0: a 502 on POST /command does not mean the command never started.
  • Egress controls — off by default (no sidecar without a network_policy); the per-sandbox policy shape; dns vs dns+nft enforcement; allow what the harness needs and test with one rollout.
  • Be a good tenant — attribution labels (and NEMO_GYM_* inside k8s), reaping a run by id with cleanup_sandboxes.py, no apt-get per sandbox, teardown off the critical path.
  • Failure signatures that are not bugs — lazy-load phantom ENOENT, startup 502 pings, terminate 404s, stale-IP 404 unknown execution, PTY zombie attachments, group-OOM 137.

opensandbox-debugging

  • Reading the sandbox's own cgroup from inside: enforced limits (cpu.max, memory.max, and what nproc sees), live usage and peak (memory.current, memory.peak, cpu.stat throttling ratio), ceiling events (memory.events), and a one-shot end-of-rollout snapshot.
  • Detecting OOM from the client: child-process kill (return_code 137, sandbox alive) vs whole-sandbox kill (SandboxBackendUnreachableError carrying the server's OOMKilled reason; sandbox.status()ERROR/STOPPED; the PTY variant), not confusing it with TimeoutError, and counting OOMs across a run from ng_agent_observations.
  • kubectl checks for those with cluster access, and a signals-to-action table.

Both pages are linked from the sandbox index (<Card>) and from each other; the provider page gets a <Tip> pointing at the best-practices guide. Pages in this folder are auto-discovered, so no nav change.

Test plan

  • Docs-only; pre-commit run --files on the changed pages (no applicable hooks fail)
  • CI fern check + preview link (verify in-page anchors resolve)

🤖 Generated with Claude Code

Field-tested guidance for running Gym environments on OpenSandbox at scale,
aimed at people bringing up their first large run:

- resources are LIMITS; scheduling requests live under
  provider_options.resource_requests — and setting only resources makes the
  server apply that map as both, so a 4 CPU / 32Gi run occupies ~30x the
  cluster capacity of a 0.5 / 512Mi one and pushes everyone's creates into
  Pending
- host-core fan-out (nproc / os.cpu_count report the host), what
  derive_cpu_env covers and what it cannot (old jest, plain make, py<3.13
  multiprocessing), and why group-OOM kills the whole sandbox
- which timeout you are actually waiting on; the N-minute failure cadence
  that is a create timeout echoing back from a full cluster
- create retries vs command_retries: a 502 on POST /command does not mean
  the command never started
- a table of failure signatures that are not bugs (lazy-load phantom ENOENT,
  startup 502 pings, terminate 404s, stale-IP 404s, PTY zombie attachments)
- tenant hygiene: attribution labels, cleaning up a run by id, no apt-get
  per sandbox, teardown off the critical path
- sizing quick reference

Linked from the sandbox index and the provider page.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Say each thing once: fold the intro note into the lead, drop the sizing
table that repeated the resources table, merge the two requests-vs-limits
justifications, and cut single-run numbers that do not change a decision.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
use_server_proxy, domain, transport_backend, max_connections,
keepalive_expiry_s, protocol — each with the failure you get when it is wrong.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
Egress is open by default (no sidecar without a network_policy); show the
per-sandbox provider_options.network_policy shape, dns vs dns+nft enforcement,
and the allow-what-the-harness-needs trap.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Replace the one-line background_exec bullet with how submit/poll/fetch works
and what each poll knob controls, including the request-rate math.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
Eight one-line practices, each linking to the section that explains it.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Promote background execution to its own section (absorbing the timeout
material) and move the failure table after tenant hygiene.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
…ction

Signed-off-by: Hemil Desai <hemild@nvidia.com>
Lead with 'check what your sandbox reports' (host cores vs LXCFS-overlaid
limits) instead of asserting one behavior; reword the short-list item to match.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
How to read the sandbox's own cgroup (limits, live usage, peak, throttling,
OOM events), how a whole-sandbox OOM surfaces to the client vs a child-process
kill, how to count OOMs across a run from rollout records, and the kubectl
checks for those with cluster access. Linked from the best-practices page and
the sandbox index.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
@hemildesai hemildesai changed the title docs(sandbox): OpenSandbox best-practices guide (requests vs limits, timeouts, non-bug failure signatures) docs(sandbox): OpenSandbox best-practices and debugging guides Sep 3, 2026
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.

1 participant