Skip to content

Toolbox daemon killed by user-space OOM/fork-storm: exec surfaces bare 502, sandbox stays zombie 'started' minutes, no errorReason #5140

Description

@kn1026

Summary

A user-space fork-storm/OOM inside a sandbox kills the toolbox daemon. The in-flight process/execute call then surfaces to the SDK as a bare 502 Bad Gateway from the toolbox proxy, and the sandbox is left in a zombie started state for several minutes before being reconciled to stopped — with errorReason: null and no audit trail. From the API consumer's side this is indistinguishable from a transient proxy blip, which sent our incident investigation in the wrong direction for hours.

Environment

  • Daytona Cloud (app.daytona.io), TypeScript SDK 0.184.0
  • Sandbox: default daytonaio/sandbox:0.8.0 snapshot, 1 vCPU / 1GB / 3GB, daemonVersion v0.201.0-b24aa9b

Reproduction

  1. Create a 1 vCPU / 1GB sandbox.
  2. sandbox.process.executeCommand(...) a command that forks a large worker pool, e.g. a Python multiprocessing.Pool(64) where each worker allocates tens of MB. (Real-world trigger: agents size pools from nproc, which reports all 64 host cores inside the container because the cgroup limits CPU time, not core visibility.)
  3. ~30–60s in, the exec rejects with a plain 502 from the toolbox proxy.
  4. GET /sandbox/{id} keeps reporting state: "started" for ~2–3 more minutes, then flips to stopped (desiredState: "stopped", errorReason: null).
  5. The in-sandbox daemon log (/tmp/daytona-daemon.log) ends abruptly mid-request — the last line is the incoming POST /process/execute — with none of the usual Received signal, shutting down gracefully lines that a normal stop writes.

Expected

Some or all of:

  1. The daemon survives user-space memory pressure — e.g. oom_score_adj protection or a dedicated memory cgroup, so a user process OOM kills the user process (exit 137 reported through the exec result), not the control plane.
  2. If the daemon does die, the API surfaces it: an errorReason like daemon crashed (possible OOM), instead of state: started → silent stopped with no reason.
  3. Faster health-check reconciliation of a daemonless sandbox (minutes of zombie started makes the 502 look like a proxy problem).
  4. Optionally: mask host cores (cpuset) or expose the real CPU allowance so nproc/os.cpu_count() inside the sandbox reflect the actual limit — that would remove the most common trigger for this class of crash entirely.

Possibly related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions