How to Configure Custom Sandbox Images for Linux, macOS, Windows, and Android in Cua

Use the immutable Image builder class in cua_sandbox/image.py to declaratively specify OS environments, package installations, and provisioning steps, then pass the configured image to Sandbox.create() or Sandbox.ephemeral() to automatically spin up the appropriate runtime backend.

Cua's sandbox system centers on an immutable, chain-able Image builder that describes what operating system and version you need, plus any additional provisioning steps like package installations, environment variables, files, and exposed ports. When you pass an Image instance to the high-level Sandbox API in cua_sandbox/sandbox.py, the runtime automatically selects the appropriate backend—whether Docker, QEMU, Lume, Android emulator, or Hyper-V—based on the image's os_type and kind.

Understanding the Image Builder Architecture

The Image class in libs/python/cua-sandbox/cua_sandbox/image.py implements an immutable builder pattern. Every mutating method returns a new Image instance, allowing you to fork configurations at any point.

  • OS-specific constructors: Static methods Image.linux(), Image.macos(), Image.windows(), and Image.android() create base images with the correct os_type, distro, version, and default kind (vm for full VMs, container for Docker).

  • Layer-based provisioning: Methods like apt_install(), brew_install(), choco_install(), apk_install(), and pwa_install() internally call _add_layer() to append provisioning steps to the image specification.

  • Runtime auto-selection: The _auto_runtime(image) function in sandbox.py examines image.os_type and image.kind to return the proper runtime implementation without manual configuration.

Configuring Linux Sandbox Images

Use Image.linux() to create Ubuntu or Debian-based sandboxes, then chain package installation methods.

from cua_sandbox import Image, Sandbox

linux_img = (
    Image.linux("ubuntu", "24.04")                # Default kind="vm"

        .apt_install("curl", "git", "build-essential")
        .pip_install("numpy", "pandas")
        .env(MY_VAR="hello", DEBUG="1")
        .run("echo 'Linux image ready'")
)

async with Sandbox.ephemeral(linux_img) as sb:
    await sb.shell.run("python -c 'import pandas; print(pandas.__version__)'")

The apt_install() method adds layers for Debian package management, while pip_install() handles Python dependencies. Environment variables set via .env() are available during sandbox provisioning and runtime.

Configuring macOS Sandbox Images

For macOS environments, Image.macos() initializes a VM configured for the Lume runtime, supporting Homebrew package management.

from cua_sandbox import Image, Sandbox

mac_img = (
    Image.macos("15")                            # macOS Sequoia 15

        .brew_install("wget", "ffmpeg")
        .env(PATH="/usr/local/bin:$PATH")
        .run("system_profiler SPSoftwareDataType")
)

async with Sandbox.ephemeral(mac_img) as sb:
    await sb.shell.run("brew list")

The brew_install() method adds layers that execute Homebrew commands during VM initialization. Note that macOS sandboxes require the Lume backend and run as full VMs (kind="vm") rather than containers.

Configuring Windows Sandbox Images

Windows images support both Chocolatey and Winget package managers via Image.windows().

from cua_sandbox import Image, Sandbox

win_img = (
    Image.windows("11")
        .choco_install("git", "7zip")
        .winget_install("Microsoft.VisualStudioCode")
        .run("echo 'Windows ready' > C:\\ready.txt")
)

async with Sandbox.ephemeral(win_img) as sb:
    await sb.shell.run("powershell -Command \"Get-Item C:\\ready.txt\"")

The choco_install() method provisions Chocolatey packages, while winget_install() handles Windows Package Manager applications. Windows sandbones run as VMs using QEMU or Hyper-V backends depending on host capabilities.

Configuring Android Sandbox Images

Android environments use Image.android() to configure emulator-based sandboxes supporting APK installation and Progressive Web App (PWA) conversion.

from cua_sandbox import Image, Sandbox

android_img = (
    Image.android("14")
        .apk_install("app-debug.apk")
        .pwa_install(
            manifest_url="https://example.com/manifest.json",
            package_name="com.example.myapp",
            builder="pwa2apk",
        )
        .run("adb shell getprop ro.build.version.release")
)

async with Sandbox.ephemeral(android_img) as sb:
    await sb.shell.run("adb shell pm list packages | grep com.example")

The apk_install() method pushes Android Package Kit files to the emulator, while pwa_install() converts web manifests to installable APKs using the specified builder tool.

Loading Custom Images from External Sources

Beyond OS-specific constructors, you can instantiate images from pre-built sources using Image.from_registry() or Image.from_file().

OCI Registry Images:

registry_img = Image.from_registry("docker.io/library/python:3.12-slim")
async with Sandbox.ephemeral(registry_img) as sb:
    await sb.shell.run("python -c 'print(\"Hello from OCI image\")'")

Local or Remote Disk Images:

custom_img = Image.from_file(
    "https://example.com/windows11.iso",
    os_type="windows",
    kind="vm",
    agent_type="osworld",            # Optional: embed OSWorld Flask server

)

async with Sandbox.ephemeral(custom_img) as sb:
    await sb.shell.run("systeminfo")

The from_file() method accepts URLs to ISO, QCOW2, or VHDX files, automatically caching and extracting them for the appropriate runtime.

Runtime Execution and Backend Selection

When you pass an image to Sandbox.create() or Sandbox.ephemeral(), the system executes three key steps:

  1. Serialization: Image.to_dict() converts the layered specification into a dictionary transmitted to the cloud API or local runtime.

  2. Cloud-init generation: Image.to_cloud_init() translates layers into cloud-init scripts for VM-based runtimes (Linux, macOS, Windows), executing package installations and configuration commands in sequence.

  3. Backend instantiation: The _auto_runtime() function selects Docker for Linux containers, QEMU for Linux/Windows VMs, Lume for macOS, or Android emulator based on os_type and kind parameters.

The immutable architecture ensures that Sandbox.ephemeral(image) creates a temporary sandbox automatically torn down after use, while Sandbox.create(image) provisions a persistent environment you can reconnect to later.

Summary

  • Use Image.linux(), Image.macos(), Image.windows(), or Image.android() to initialize OS-specific base images in cua_sandbox/image.py.
  • Chain provisioning methods like apt_install(), brew_install(), choco_install(), or apk_install() to add immutable layers defining package state.
  • Specify execution kind with .with_kind("container") or constructor parameters to choose between Docker containers and full VMs.
  • Load external images via Image.from_registry() for OCI images or Image.from_file() for custom disk images.
  • Let the runtime auto-select the appropriate backend by passing your configured image to Sandbox.create() or Sandbox.ephemeral() in cua_sandbox/sandbox.py.

Frequently Asked Questions

What's the difference between VM and container kinds?

The kind parameter determines whether the sandbox runs as a full virtual machine or a container. In cua_sandbox/sandbox.py, the _auto_runtime() function maps kind="container" to Docker for Linux images, while kind="vm" selects QEMU for Linux/Windows, Lume for macOS, or Android emulator for Android images. VMs provide full kernel isolation and support GUI applications, while containers offer lighter-weight process isolation.

Can I use custom disk images not from the standard constructors?

Yes. Use Image.from_file(path, os_type="...", kind="...") to load local ISO, QCOW2, or VHDX files, or specify a URL to automatically download and cache remote disk images. You must explicitly provide os_type and kind parameters so the runtime knows which backend to initialize and how to provision the image.

How does the runtime know which backend to use?

The _auto_runtime() function in cua_sandbox/sandbox.py examines the Image instance's os_type (linux, macos, windows, android) and kind (vm, container) attributes. It returns the appropriate runtime implementation—Docker for Linux containers, QEMU for Linux/Windows VMs, Lume for macOS VMs, or Android emulator—without requiring manual backend configuration.

What's the difference between Sandbox.create() and Sandbox.ephemeral()?

Sandbox.create(image) provisions a persistent sandbox that remains running until explicitly stopped, allowing you to disconnect and reconnect to the same environment. Sandbox.ephemeral(image) creates a temporary sandbox suitable for async context managers (async with), automatically tearing down the environment and freeing resources when the context exits, making it ideal for CI/CD pipelines and one-off tasks.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →