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(), andImage.android()create base images with the correctos_type,distro,version, and defaultkind(vmfor full VMs,containerfor Docker). -
Layer-based provisioning: Methods like
apt_install(),brew_install(),choco_install(),apk_install(), andpwa_install()internally call_add_layer()to append provisioning steps to the image specification. -
Runtime auto-selection: The
_auto_runtime(image)function insandbox.pyexaminesimage.os_typeandimage.kindto 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:
-
Serialization:
Image.to_dict()converts the layered specification into a dictionary transmitted to the cloud API or local runtime. -
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. -
Backend instantiation: The
_auto_runtime()function selects Docker for Linux containers, QEMU for Linux/Windows VMs, Lume for macOS, or Android emulator based onos_typeandkindparameters.
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(), orImage.android()to initialize OS-specific base images incua_sandbox/image.py. - Chain provisioning methods like
apt_install(),brew_install(),choco_install(), orapk_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 orImage.from_file()for custom disk images. - Let the runtime auto-select the appropriate backend by passing your configured image to
Sandbox.create()orSandbox.ephemeral()incua_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →