Skip to main content

Pausing and Resuming

OpenKruise Agents allows you to pause a running sandbox so that it stops consuming CPU / memory, and later resume it back to a running state, keeping the sandbox identity (same sandboxID, same Pod) intact.

âš ī¸ This area is still evolving. The underlying capture mechanism (whether memory state is preserved, whether the filesystem is checkpointed, etc.) depends on the cluster environment (e.g. memory-state preservation is currently only supported on Alibaba Cloud ACS). This document focuses on how to use the API; for state-preservation guarantees, see your platform runbook.

Overview​

Two parallel interfaces are provided, both acting on the same underlying Sandbox resource:

InterfaceAudienceTypical scenarios
E2B SDK (pause/connect)Application code running on top of E2B Python / JavaScriptProgrammatic pause before idle, resume on demand
Kubernetes CRDCluster ops, declarative GitOps, kubectl or custom controlToggle spec.paused or schedule spec.pauseTime

Pause/Resume is one-to-one: the sandbox ID stays the same across the pause → resume cycle. If you need a one-to-many "snapshot and fork" workflow, see Snapshot Management.

How It Works (summary)​

  1. Pause freezes the sandbox Pod. Active WebSocket / PTY / command-stream connections are dropped; clients must reconnect after resume.
  2. Resume brings the Pod back to the running state. The sandbox ID is preserved.
  3. The exact capture scope (memory / filesystem) depends on the runtime platform and the configuration on the backing SandboxSpec.

Pausing a Sandbox​

The E2B SDK exposes a pause() method on a sandbox handle. It calls the POST /sandboxes/{sandboxID}/pause endpoint under the hood.

from e2b_code_interpreter import Sandbox

with Sandbox.create(template="code-interpreter", timeout=300) as sbx:
sbx.run_code("a = 1")
sbx.pause() # sandbox is now paused; sandboxID is retained
import { Sandbox } from 'e2b'

const sbx = await Sandbox.create({ template: 'code-interpreter', timeoutMs: 300_000 })
await sbx.betaPause()

Notes:

  • Pausing a sandbox that is not in running state returns 409 Conflict.
  • The lifetime of a paused sandbox is controlled separately from its running timeout. sandbox-manager uses the forever paused-retention policy by default; you can configure a shorter window as described in Retaining a Paused Sandbox.

Auto Pause​

In addition to calling pause explicitly, you can declare that the sandbox should auto-transition into paused on expiry at creation time — when the timer fires the sandbox is not killed, it moves into the paused state with its identity preserved, waiting for a later resume.

Follow the E2B docs — Auto-pause: set lifecycle.on_timeout to "pause" when creating the sandbox.

from e2b_code_interpreter import Sandbox

sbx = Sandbox.create(
template="demo",
timeout=600, # 10 minutes; on expiry go to paused instead of being killed
lifecycle={
"on_timeout": "pause",
"auto_resume": True,
},
)
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create({
template: 'demo',
timeoutMs: 10 * 60 * 1000, // 10 minutes; on expiry go to paused
lifecycle: {
onTimeout: 'pause',
autoResume: true,
},
})

With auto_resume enabled, an incoming request routed through sandbox-gateway resumes a paused sandbox before the request is forwarded. The gateway configuration must enable enable-wake-on-traffic; otherwise the request does not wake the sandbox.

Waking on Ingress Traffic​

Wake-on-traffic is useful for auto-paused sandboxes that should become available again on the next data-plane request. The E2B auto_resume option is persisted as spec.autoPausePolicy.resume.onIngressTraffic:

apiVersion: agents.kruise.io/v1alpha1
kind: Sandbox
metadata:
name: my-sandbox
namespace: default
spec:
autoPausePolicy:
resume:
onIngressTraffic:
pauseTimeout: 5m

When pauseTimeout is positive and the sandbox already uses auto-pause, a successful traffic wake schedules the next pause for that duration after the wake. Omit it to leave the resumed sandbox running until another pause or deletion. Concurrent requests share the same resume operation. The triggering request waits for the sandbox to become ready and fails if resume times out; clients should retry failed requests according to their normal idempotency policy.

Retaining a Paused Sandbox​

reserve-paused-sandbox-duration controls how long a timed Sandbox is retained after its pause transition is applied. When the Sandbox pauses, OpenKruise Agents recalculates spec.shutdownTime as:

pause transition time + reserve-paused-sandbox-duration

The value must be a positive Go duration, such as 30m, 2h, or 168h. The special value forever selects the built-in 100-year retention window; it does not create a truly infinite deadline. Values such as 0, negative durations, and 1d are invalid (time.ParseDuration does not support a d unit).

This policy only recalculates an existing timeout. A Sandbox created with the never-timeout extension has no shutdownTime, so paused retention does not introduce one.

Set the persisted retention policy at creation time with the e2b.agents.kruise.io/reserve-paused-sandbox-duration metadata extension. It applies to auto-pause and to later manual pauses:

from e2b_code_interpreter import Sandbox

sbx = Sandbox.create(
template="demo",
timeout=600,
lifecycle={"on_timeout": "pause", "auto_resume": False},
metadata={
"e2b.agents.kruise.io/reserve-paused-sandbox-duration": "2h",
},
)

To override the persisted value for a manual pause, pass the x-e2b-kruise-reserve-paused-sandbox-duration request header. The accepted override is persisted for subsequent pause operations when that request performs the running-to-paused transition:

sbx.pause(
headers={
"x-e2b-kruise-reserve-paused-sandbox-duration": "30m",
}
)

The resolution order is pause request header → persisted policy → forever default. If the creation metadata or pause header contains an invalid value, sandbox-manager returns 400 Bad Request. Manager-created Sandboxes without an explicit value use forever, which is persisted as the internal agents.kruise.io/reserve-paused-sandbox-duration annotation.

Resuming a Sandbox​

The recommended interface on the E2B SDK side is Sandbox.connect(...) — it implicitly resumes a paused sandbox and at the same time refreshes its timeout. The legacy resume endpoint still exists for backward compatibility but should not be used by new code.

from e2b_code_interpreter import Sandbox

sbx = Sandbox.connect(sandbox_id, timeout=300) # resumes if paused, and extends the timeout
sbx.run_code("print(a)")
import { Sandbox } from 'e2b'

const sbx = await Sandbox.connect(sandboxId, { timeoutMs: 300_000 })

Notes:

  • If the sandbox is already running, connect only refreshes the timeout. The refresh is extend-only: the timeout will never be shortened. (Exception: a paused → running resume applies the requested timeout directly.)
  • connect on a sandbox that does not exist or is owned by another API key returns 404 Not Found.

Capability Matrix​

CapabilityE2B SDKKubernetes CRD
Pause a running sandbox✅ sbx.beta_pause()✅ spec.paused: true
Resume a paused sandbox✅ Sandbox.connect(id, ...)✅ spec.paused: false
Auto-pause when the timeout expires✅ lifecycle.on_timeout='pause'❌
Auto-pause at a specific absolute time❌✅ spec.pauseTime
Auto-resume a paused sandbox on ingress traffic✅ lifecycle.auto_resume=true✅ spec.autoPausePolicy.resume.onIngressTraffic
Configure deletion after entering paused✅ creation metadata or pause header✅ annotation for auto-pause; write spec.shutdownTime for a manual pause
Set / refresh the sandbox timeout together with resume✅ Sandbox.connect(id, timeout=...)✅ write spec.shutdownTime / spec.pauseTime in the same patch
Extend-only guard on timeout refresh while running✅❌ user-written value may shorten
Observe paused/running statevia SDK responsestatus.phase (Paused / Running)

If you need "extend-only, never shorten" timeout semantics, use the E2B SDK. The CRD path is better suited for declarative / GitOps control over the paused/running bit plus absolute scheduling times.

Notes​

  • Connection drop. The Pod is frozen on pause; all active streams (WebSocket / PTY / command streams) disconnect. Clients must reconnect after resume.
  • Timeout during pause. The running timeout does not continue counting down unchanged after pause. Auto-delete is controlled by spec.shutdownTime, which paused retention can recalculate from the pause transition time.
  • Old SDKs. The legacy POST /sandboxes/{sandboxID}/resume endpoint is kept for old SDK compatibility only. New code should always use Sandbox.connect(...).
  • State-preservation caveats. Whether memory is preserved across pause/resume depends on the runtime platform. If you need explicit memory + filesystem snapshots that can also be cloned into brand-new sandboxes, use Snapshot Management instead.