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

> Learn to configure custom sandbox images for Linux macOS Windows and Android using Cua's immutable Image builder. Effortlessly spin up specific OS environments for your testing needs.

- Repository: [Cua/cua](https://github.com/trycua/cua)
- Tags: how-to-guide
- Published: 2026-04-27

---

**Use the immutable `Image` builder class in [`cua_sandbox/image.py`](https://github.com/trycua/cua/blob/main/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`](https://github.com/trycua/cua/blob/main/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`](https://github.com/trycua/cua/blob/main/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`](https://github.com/trycua/cua/blob/main/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.

```python
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.

```python
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()`.

```python
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.

```python
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:**

```python
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:**

```python
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`](https://github.com/trycua/cua/blob/main/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`](https://github.com/trycua/cua/blob/main/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`](https://github.com/trycua/cua/blob/main/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`](https://github.com/trycua/cua/blob/main/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.