Quicksand is an async Python API to launch, control, and snapshot QEMU virtual machines with a particular focus on sandboxing AI agents. Quicksand provides pre-built Linux VMs for Ubuntu and Alpine distros. It works on x86_64 and ARM64 across macOS, Linux, and Windows with no root privileges, no Docker, and no system dependencies. Just pip install quick-sandbox.
pip install 'quick-sandbox[qemu,alpine]'Or install the core package and add QEMU/images separately:
pip install quick-sandbox
quicksand install qemu alpineimport asyncio
from quicksand import Sandbox
async def main():
async with Sandbox(image="ubuntu") as sb:
result = await sb.execute("echo 'Hello from the sandbox!'")
print(result.stdout)
asyncio.run(main())result = await sb.execute("apt update && apt install -y python3")
print(result.stdout, result.exit_code)Share host directories into the VM at boot or on the fly.
# At boot
async with Sandbox(
image="ubuntu",
mounts=[Mount("./workspace", "/mnt/workspace")],
) as sb:
...
# Or dynamically on a running sandbox
handle = await sb.mount("/tmp/data", "/mnt/data")
await sb.execute("ls /mnt/data")
await sb.unmount(handle)Sandboxes are network-isolated by default. Opt in to internet access and port forwarding with NetworkMode.FULL.
async with Sandbox(
image="ubuntu",
network_mode=NetworkMode.FULL,
port_forwards=[PortForward(host=8080, guest=80)],
) as sb:
...Save the VM's disk state to a directory. Load it later, even on a different machine.
await sb.execute("pip install numpy pandas")
await sb.save("my-env") # VM keeps running
# Load later
async with Sandbox(image="my-env") as sb:
await sb.execute("python3 -c 'import numpy; print(numpy.__version__)'")Capture the full VM state and roll back if something goes wrong.
await sb.checkpoint("before-experiment")
await sb.execute("apt install -y something-risky")
await sb.revert("before-experiment") # the VM snaps back to the checkpointDesktop images provide a full Xfce4 graphical environment with a browser. Install one with quicksand install ubuntu-desktop or quicksand install alpine-desktop.
async with Sandbox(image="ubuntu-desktop", enable_display=True) as sb:
await sb.screenshot("screen.png")
await sb.type_text("hello world")
await sb.press_key(Key.RET)
await sb.mouse_move(500, 300)
await sb.mouse_click("left")Here are all of the Sandbox configuration options:
Sandbox(
# Image or save name to boot
image="ubuntu",
# Guest RAM (default: "512M")
memory="2G",
# Virtual CPU cores (default: 1)
cpus=4,
# Host directories shared into the VM at boot
mounts=[Mount("/host", "/guest")],
# NONE, MOUNTS_ONLY (default), or FULL internet access
network_mode=NetworkMode.FULL,
# Forward host TCP ports into the guest
port_forwards=[PortForward(host=8080, guest=80)],
# Expand the guest filesystem on boot
disk_size="10G",
# Attach virtual GPU, keyboard, and mouse for screenshot/type_text/mouse control
enable_display=True,
# Auto-save VM state on stop
save="my-save-name",
)| Image | Type | Wheel size | Install command | What is it |
|---|---|---|---|---|
ubuntu |
Base | ~341 MB | quicksand install ubuntu |
Ubuntu 24.04 headless |
alpine |
Base | ~78 MB | quicksand install alpine |
Alpine 3.23 headless (faster boot) |
ubuntu-desktop |
Overlay (ubuntu) |
~263 MB | quicksand install ubuntu-desktop |
Ubuntu 24.04 + Xfce4 + Firefox |
alpine-desktop |
Overlay (alpine) |
~310 MB | quicksand install alpine-desktop |
Alpine 3.23 + Xfce4 + Chromium |
quicksand-agent |
Overlay (ubuntu) |
~304 MB | quicksand install quicksand-agent |
Ubuntu + Python 3.12, uv, build-essential, requests, pyyaml, ddgs, markitdown |
quicksand-cua |
Overlay (quicksand-agent) |
~445 MB | quicksand install quicksand-cua |
Agent Sandbox + Xvfb, x11vnc, noVNC, Playwright, Chromium |
git clone https://github.com/microsoft/quicksand.git
cd quicksand
uv sync
uv run uvr build --all-packages| Topic | Guide | Under the Hood |
|---|---|---|
| Installation | Installing packages | QEMU binaries, kernels, qcow2 disks |
| Sandbox Lifecycle | Creating and configuring sandboxes | -m, -smp, -accel, machine types |
| Running Commands | execute(), streaming, exit codes |
Kernel boot, agent tokens, hostfwd |
| File Exchange | Mounts, hot-mounts, getting data in/out | CIFS via guestfwd, 9p via -fsdev |
| Save and Rollback | Checkpoints, reverts, persistent saves | qcow2 overlays, savevm, blockdev-snapshot-sync |
| Desktop Control | Screenshots, keyboard, mouse | VNC, GPU, USB tablet, QMP input injection |
| Network and Isolation | Network modes, port forwarding | SLIRP NAT, restrict=on, guestfwd |
| Performance | What makes it fast | io_uring, IOThreads, TCG vs KVM |
| Guide | When to use |
|---|---|
| Creating Images | Build a new base or overlay image package |
| Extending the Sandbox | Add a method, OS, architecture, or QEMU flag |
| Testing | Run or write tests |
| Releasing | Cut a release |
Full guides: User Guide | Under the Hood | Contributor Guide
