Choose and build an image
See every image Cua publishes, pick a canonical or registry image, add packages and files, and pull private images, locally or in your own cloud.
A sandbox runs one image reference. One resolver picks the variant each backend runs (container rootfs or VM disk) and pins its digest, so the same reference works locally or in your own cloud.
Canonical images#
from cua_sandbox import Image
Image.linux() # ghcr.io/trycua/linux:24.04, Ubuntu 24.04 desktop with cua-spacesd
Image.windows() # ghcr.io/trycua/windows:2022, Windows Server 2022 VM
Image.macos() # ghcr.io/trycua/macos:26 (Tahoe); Image.macos("15") for Sequoia| Image | Local |
|---|---|
Image.linux() | Container (gVisor when installed) |
Image.linux(kind="vm") | QEMU VM from linux:24.04-disk |
Image.windows() | QEMU VM from windows:2022-disk |
Image.macos() | Lume VM (Apple silicon) |
- The Linux, Windows and macOS 26 images ship cua-spacesd, so they get the computer interfaces. macOS 15 runs the legacy guest agent: reach it through services you declare.
CUA_IMAGE_LINUX,CUA_IMAGE_WINDOWSandCUA_IMAGE_MACOSoverride the defaults. The CLI takes the aliaseslinux,windows,macos,macos:tahoeandmacos:sequoia.- Windows images do not include a Windows license. Confirm that your own licenses cover your use, including hosting and virtualization rights.
Every published image#
Cua publishes these images on ghcr.io/trycua. The Linux, Windows, benchmark
and macOS 26 images include cua-spacesd and
cua-driver, so they are ready for agentic workloads. Windows,
macOS and tags ending in -disk are VMs; the rest are containers. For each
image's runtimes and limits, see the
image catalog details.
Linux#
ghcr.io/trycua/linux:24.04ghcr.io/trycua/linux:24.04-disk(VM)ghcr.io/trycua/linux:24.04-slimghcr.io/trycua/linux:24.04-slim-disk(VM)ghcr.io/trycua/omarchy:edgeghcr.io/trycua/omarchy:edge-disk(VM)
Windows#
ghcr.io/trycua/windows:2022
macOS#
ghcr.io/trycua/macos:26(Tahoe)ghcr.io/trycua/macos:26-slim(Tahoe 26 slim)ghcr.io/trycua/macos:15(Sequoia)
Adapter benchmark images#
ghcr.io/trycua/bench-web:1.0(run MiniWoB++, WebVoyager, Online-Mind2Web, WebGym)ghcr.io/trycua/bench-web:1.0-disk(VM)ghcr.io/trycua/bench-osworld:verified(run OSWorld-Verified)ghcr.io/trycua/bench-osworld:verified-disk(VM)
To start one, install the CLI. Then run cua sb create <image> --name desk
and open its desktop with cua sb view desk (see
Any registry image).
curl -fsSL https://cua.ai/install.sh | shAny registry image#
image = Image.from_registry("python:3.12-slim") # short refs mean docker.io
image = Image.from_registry("ghcr.io/acme/app@sha256:...")
windows = Image.from_registry("registry.example/win:1", os_type="windows", kind="vm")kindis detected: a container image runs as a container, a containerDisk (a disk at/disk/disk.img) as a VM.os_type="windows"selects EFI firmware; a Windows disk never boots on BIOS.- Pin a digest when every sandbox must boot the same content.
cua sb create linux --name desk
cua sb create python:3.12-slim --name app -- python -m http.server 8000Add packages, files and settings#
Each builder method returns a new, immutable Image. Steps build into your
container engine before boot (VM images apply them after boot through
cua-spacesd). To build once and reuse an image, use
cua image build and cua image push.
from cua_sandbox import Image
Image.linux().apt_install('curl', 'git', 'ffmpeg')
Image.macos().brew_install('ffmpeg', 'jq')
Image.windows().choco_install('nodejs', 'git')
Image.windows().winget_install('Microsoft.VisualStudioCode')img = (
Image.linux()
.apt_install('curl', 'git', 'ffmpeg', 'python3-pip') # Image.linux() has no pip
.pip_install('requests', 'Pillow') # or .uv_install(...)
.env(LOG_LEVEL='debug') # not for secrets
.copy('./config.json', '/app/config.json')
.run('mkdir -p /app/data')
.expose(8080) # prefer services={"app": 8080}
)Fork a base and start the result like any image:
base = Image.linux().apt_install('curl', 'git', 'python3-pip')
dev = base.pip_install('ipython', 'rich').env(DEBUG='1')
prod = base.pip_install('gunicorn').run('useradd -m appuser')from cua_sandbox import Sandbox
async with Sandbox.ephemeral(dev) as sb:
print((await sb.shell.run('python -c "import rich"')).returncode)img = Image.linux().apt_install('curl').pip_install('requests')
print(img.to_dict()) # the spec
# {'os_type': 'linux', 'distro': 'ubuntu', 'version': '24.04', 'kind': None,
# 'layers': [{'type': 'apt_install', 'packages': ['curl']},
# {'type': 'pip_install', 'packages': ['requests']}]}Image.from_file() boots a local qcow2, vhdx, raw or ISO disk (URLs are
downloaded and cached):
Image.from_file('/path/to/disk.qcow2', os_type='linux')
Image.from_file('https://example.com/windows.vhdx', os_type='windows')Private registries#
Pass a RegistrySecret. The SDK uses it for the pull. Credentials are never logged or saved in ~/.cua.
from cua_sandbox import Image, RegistrySecret, Sandbox
image = Image.from_registry(
"ghcr.io/acme/agent:1.4",
secret=RegistrySecret.from_env(), # CUA_REGISTRY_USERNAME / CUA_REGISTRY_PASSWORD
)
sb = await Sandbox.create(image)| Secret | Credentials |
|---|---|
RegistrySecret(username, password) | Explicit values; a token works as the password |
RegistrySecret.from_env(username_var=..., password_var=...) | Read from the environment at create time |
RegistrySecret.aws_ecr(region=None) | A private Amazon ECR image, via the AWS CLI |
Without a secret, local pulls also use CUA_REGISTRY_*, GITHUB_TOKEN for
ghcr.io, your Docker config and ECR credentials.
cua sb create ghcr.io/acme/agent:1.4 \
--registry-secret env:CUA_REGISTRY_USERNAME:CUA_REGISTRY_PASSWORD
cua sb create 123456789012.dkr.ecr.us-east-1.amazonaws.com/agent:1.4 \
--registry-secret aws-ecr:us-east-1import { registrySecret, SandboxCreateOptions } from '@trycua/cua';
SandboxCreateOptions.create({
image: 'ghcr.io/acme/agent:1.4',
registrySecret: registrySecret.fromEnv('CUA_REGISTRY_USERNAME', 'CUA_REGISTRY_PASSWORD'),
});