Lifecycle#

A sandbox moves between a few well-defined states. The important ones for your bill are running (billed per second) and paused ($0 compute, full RAM and disk state kept).

States#

                    (priority: "batch" only)
                         ┌─────────┐
                         │ queued  │  waiting for capacity, not billed
                         └────┬────┘
                              ▼
   POST /sandboxes ──► ┌──────────┐
                       │ creating │  not billed
                       └────┬─────┘
                            ▼
                       ┌──────────┐  pause / auto-pause  ┌─────────┐       ┌────────┐
          ┌──────────► │ running  │ ───────────────────► │ pausing │ ────► │ paused │
          │            └──────────┘                      └─────────┘       └───┬────┘
          │                                                                    │ resume /
          │            ┌──────────┐                                            │ wake on request
          └─────────── │ resuming │ ◄──────────────────────────────────────────┘
                       └──────────┘

   any state ── DELETE / ttl expired ──► deleting ──► deleted
   any state ── VM failure ──────────► error   (see sandbox.error)
State Meaning
queued priority: "batch" only; waiting for capacity
creating VM is being started from a template, snapshot or image
running VM is running; exec, files, ports, SSH and desktop work
pausing RAM and disk are being snapshotted at the same instant
paused VM is stopped; RAM and disk are kept in a snapshot
resuming VM is being restored (target p95 < 800 ms)
deleting, deleted sandbox and its data are being removed / gone
error VM failed; sandbox.error has code and message

Processes, open TCP connections, terminal sessions, desktop windows and browser tabs survive a pause: resume restores memory, it does not reboot.

Pause and resume#

await sandbox.pause();   // waits until "paused" (wait: true by default)
await sandbox.resume();  // waits until "running"
sandbox.pause()
sandbox.resume()
curl -sS -X POST https://api.task.cloud/v1/sandboxes/sbx_2fqxnt4bkq7m/pause \
  -H "Authorization: Bearer $TASKCLOUD_API_KEY" -H "Content-Type: application/json" \
  -d '{"wait": false}'    # 202, state "pausing"
taskcloud pause sbx_2fqxnt4bkq7m
taskcloud resume sbx_2fqxnt4bkq7m

With wait: true (default) the call blocks until the transition finishes (max 60 s, then 202). Pausing a paused sandbox or resuming a running one is a no-op that returns 200.

Auto-pause#

Every sandbox pauses itself after auto_pause.idle_timeout_seconds without activity. Defaults: enabled: true, idle_timeout_seconds: 300 (5 minutes). Range: 60–86400 seconds.

Activity means any of:

  • a running exec command or an open PTY session;
  • inbound traffic on an exposed port or over SSH;
  • a connected desktop viewer, or desktop input;
  • CPU usage above a low threshold.

An open file watch (GET .../files/watch) does not count as activity.

await sandbox.update({ auto_pause: { enabled: true, idle_timeout_seconds: 900 } });
sandbox.update(auto_pause={"enabled": True, "idle_timeout_seconds": 900})
curl -sS -X PATCH https://api.task.cloud/v1/sandboxes/sbx_2fqxnt4bkq7m \
  -H "Authorization: Bearer $TASKCLOUD_API_KEY" -H "Content-Type: application/json" \
  -d '{"auto_pause":{"enabled":true,"idle_timeout_seconds":900}}'

taskcloud update sbx_2fqxnt4bkq7m --idle-timeout 15m
taskcloud update sbx_2fqxnt4bkq7m --no-auto-pause

Wake on request#

You rarely need to call resume. A paused sandbox wakes automatically when it receives:

  • an HTTP request to one of its port URLs (https://<short_id>-<port>.taskhost.cloud) or its desktop URL;
  • an SSH connection through ssh.taskhost.cloud;
  • an API call that needs a running VM: exec, PTY, files, screenshot, input.

The request waits for the resume (target p95 < 800 ms) and then completes normally, without an error on the client side. GET /sandboxes/{sandbox_id} does not wake a sandbox.

To fail fast instead, pass auto_resume=false on API calls; a paused sandbox then returns 409 sandbox_paused:

curl -sS -X POST "https://api.task.cloud/v1/sandboxes/sbx_2fqxnt4bkq7m/exec?auto_resume=false" \
  -H "Authorization: Bearer $TASKCLOUD_API_KEY" -H "Content-Type: application/json" \
  -d '{"command":"uptime"}'

Always-on#

always_on: true disables auto-pause. Instead of hourly billing, the sandbox costs a flat $19.90 per month (prorated). Available for size default only.

await sandbox.update({ always_on: true });
taskcloud update sbx_2fqxnt4bkq7m --always-on

TTL#

ttl_seconds deletes the sandbox automatically N seconds after creation (min 60; null keeps it until you delete it). expires_at shows the deadline. Change it with PATCH. Expiry sends sandbox.deleted with reason: "ttl_expired".

Batch priority#

priority: "batch" is billed at −50%. In exchange the sandbox may wait in queued until capacity is available, and the create call always returns 202 (wait is ignored). Use it for evals, CI and other jobs that are not interactive. Work run during the EU night window (22:00–07:00 CET) gets the same −50% automatically.

const job = await client.sandboxes.create({ template: "base", priority: "batch", wait: false });
await job.waitFor("running");
job = client.sandboxes.create(template="base", priority="batch", wait=False)
job.wait_for("running")
taskcloud create --template base --batch --no-wait

Billing per state#

Compute is billed per second while the VM runs. Prices per active hour: small $0.015, default $0.030, large $0.060.

State Compute Storage
queued, creating not billed –
running per second at the size rate disk above the included amount: $0.05/GB-month
pausing, resuming per second, as running –
paused $0 pause snapshot free for 30 days, then $0.03/GB-month; extra disk as above
deleting, deleted not billed not billed
error not billed disk kept until you delete the sandbox
always_on: true flat $19.90/month (default) instead of per second as above

Batch priority and the off-peak window (22:00–07:00 CET) are billed at −50%. See Billing for plans and credit.

Checkpoints and host failures#

TaskCloud saves sandboxes to replicated object storage, encrypted with a key of your account:

  • Pause: a paused sandbox's memory and disk are uploaded a few minutes after the pause. It can then resume on any host, even if its original host fails.
  • Running sandboxes: after about 15 minutes of activity since the last resume or checkpoint, TaskCloud takes a disk checkpoint. The sandbox keeps running; there is no pause. The checkpoint is crash-consistent, like the disk after a power cut: files flushed to disk are in it, data still in the guest's page cache may not be.

If the host running a sandbox fails, TaskCloud restores the sandbox on another host from its newest saved state and sends sandbox.restored_from_checkpoint:

Newest saved state After the restore
pause snapshot (kind: memory) memory, processes and files as of that pause
disk checkpoint (kind: disk, ram_lost: true) files as of the checkpoint; the sandbox booted, so processes and /dev/shm restarted

At most about 15 minutes of disk work is lost (data_loss_window_seconds in the webhook tells you how much). Long-running agents should keep their progress in files (and call sync after important steps) so they can continue from a disk checkpoint. Paused sandboxes are not affected: their pause snapshot is restored as is.

A sandbox paused less than a few minutes before its host failed may not be uploaded yet. It stays paused until the host returns.

Webhooks per transition#

Event Sent when Typical reason
sandbox.ready sandbox first becomes running after create or fork api
sandbox.paused state becomes paused api, auto_pause, spend_limit, host_maintenance
sandbox.resumed state becomes running after resuming api, wake_on_request
sandbox.error state becomes error an error code
sandbox.deleted state becomes deleted api, ttl_expired
sandbox.restored_from_checkpoint sandbox runs again from an older saved state after a host failure host_lost, restore_interrupted

Each event carries previous_state, state, reason and the full sandbox. Setup and signature verification: Webhooks.