Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/trycua/cua/llms.txt

Use this file to discover all available pages before exploring further.

Cua Sandbox offers three lifecycle patterns. Ephemeral sandboxes are automatically destroyed when the async with block exits — ideal for CI, tests, and one-off tasks where state has no value after the job. Persistent sandboxes outlive the creating process and can be reconnected to by name, which is useful for long-running development environments or multi-step workflows split across separate scripts. Connect mode attaches to an already-running sandbox without starting or stopping it.

Ephemeral sandboxes

Use Sandbox.ephemeral() as an async context manager. The sandbox is destroyed automatically when the block exits, whether the block completes normally or raises an exception.
import asyncio
from cua import Sandbox, Image

async def main():
    async with Sandbox.ephemeral(Image.linux(), local=True) as sb:
        result = await sb.shell.run("echo hello")
        print(result.stdout)   # hello
    # sandbox is destroyed here

asyncio.run(main())
Sandbox.ephemeral is the best default for scripts, CI pipelines, and agent tasks where reproducibility matters more than preserving state between runs.

Ephemeral with cloud resources

import os
from cua import Sandbox, Image

async def main():
    async with Sandbox.ephemeral(
        Image.linux(),
        api_key=os.environ["CUA_API_KEY"],
        cpu=4,
        memory_mb=8192,
        disk_gb=50,
        region="us-east-1",
    ) as sb:
        result = await sb.shell.run("nproc")
        print(result.stdout)  # 4

Persistent sandboxes

Use Sandbox.create() when the sandbox must outlive the script. Call await sb.disconnect() to drop the connection while leaving the sandbox running, then reconnect later by name.
1

Create and name the sandbox

from cua import Sandbox, Image

sb = await Sandbox.create(
    Image.linux(),
    name="my-dev-sandbox",
    local=True,
)
await sb.shell.run("apt-get install -y vim")
await sb.disconnect()   # connection dropped, sandbox keeps running
2

Reconnect from a different script

async with Sandbox.connect("my-dev-sandbox", local=True) as sb:
    result = await sb.shell.run("vim --version")
    print(result.stdout)
# connection dropped again, sandbox still running
3

Destroy the sandbox when done

async with Sandbox.connect("my-dev-sandbox", local=True) as sb:
    await sb.destroy()   # permanently deleted

disconnect vs destroy vs delete

These three operations are distinct. Choosing the wrong one can silently discard work or leave a sandbox consuming resources.
MethodEffectUse when
await sb.disconnect()Drops the network connection; sandbox keeps runningYou will reconnect later
await sb.destroy()Disconnects and permanently deletes the sandboxYou are finished with this sandbox
await Sandbox.delete(name)Permanently deletes by name, no connection neededYou want to clean up from a different process
# Delete by name without connecting first
await Sandbox.delete("my-dev-sandbox", local=True)

Reconnecting to a running sandbox

Sandbox.connect() supports both await and async with. The context manager calls disconnect() on exit — the sandbox is not destroyed.
# Context manager form (recommended)
async with Sandbox.connect("my-sandbox", local=True) as sb:
    await sb.shell.run("echo reconnected")
# connection dropped, sandbox keeps running

# Plain await form (remember to disconnect manually)
sb = await Sandbox.connect("my-sandbox", local=True)
await sb.shell.run("echo reconnected")
await sb.disconnect()
Sandbox.connect never starts or stops the sandbox. It only manages the control connection. If the sandbox has exited, connect will raise an error.

Listing running sandboxes

Use Sandbox.list() to enumerate all running and suspended sandboxes. Filter by local=True to list only sandboxes on the local runtime.
sandboxes = await Sandbox.list(local=True)
for info in sandboxes:
    print(info.name, info.os_type, info.status)
SandboxInfo fields include name, status, os_type, host, vnc_url, api_url, and created_at.

Suspending and resuming

Suspend a sandbox to save its state to disk without destroying it. Resume picks up from the saved state.
# Save state
await Sandbox.suspend("my-sandbox", local=True)

# Resume from saved state — returns a connected Sandbox
sb = await Sandbox.resume("my-sandbox", local=True)
await sb.shell.run("echo I survived a suspend")
On local QEMU runtimes, suspend uses a QMP snapshot. On Docker it uses docker pause. On Lume it performs a stop. Cloud suspend calls the cua.ai API.

Choosing a local runtime

Pass local=True to any lifecycle method to run on your own machine. Cua automatically selects the correct runtime backend for the image type.
# Docker container (fast startup)
async with Sandbox.ephemeral(Image.linux(kind="container"), local=True) as sb:
    ...

# QEMU Linux VM (full kernel isolation)
async with Sandbox.ephemeral(Image.linux(), local=True) as sb:
    ...

# macOS VM (Apple Silicon only, requires Lume)
async with Sandbox.ephemeral(Image.macos(), local=True) as sb:
    ...

Build docs developers (and LLMs) love