Run Omarchy on Fleet
Provision an amd64 Omarchy desktop on Cua Fleet and control it with the Cua Sandbox SDK.
Use the Cua Sandbox SDK to provision an amd64 Omarchy desktop on
Cua Fleet. Fleet runs the Omarchy system
as a KubeVirt containerDisk; the guest starts Hyprland and exposes both the
cua-computer-server API and the Cua Driver MCP service.
For the desktop stack and input boundaries, see Linux desktops and computer use and Hyprland support. Provisioning a private VM does not establish isolated background input between applications inside that VM.
This guide is for the amd64 Fleet image. The Omarchy on Apple Silicon guide builds a separate ARM64 VM for local Lume use and does not run on Fleet.
Two Cua Driver sessions select cells in LibreOffice Calc and objects in Inkscape while a terminal stays in the foreground. Recorded with Cua Driver and the Hyprland plugin 0.24.0. This 50-second clip demonstrates background selections, not data edits or continuous foreground-isolation testing.
Before you start#
You need:
- Python
>=3.11,<3.14; uv;cua-sandbox==0.7.0; and- a Fleet access token or OAuth client credentials that can manage pools.
The Fleet-verified public image reference is:
public.ecr.aws/k5j5w0x5/cua-omarchy-workspace@sha256:dc448dcc986f443980dfb3586f1b91d8699091ad96a74b369016535f0dbb0342The image is an amd64 KubeVirt containerDisk. It is not an ordinary OCI
application image: the image contains a bootable disk at /disk/disk.img.
Fleet pulls the public image, so you do not need AWS credentials on the
machine that runs this script.
Authenticate with Fleet#
The SDK connects to https://run.cua.ai by default. Export one supported
credential set before running the example:
export FLEETS_TOKEN="<your-access-token>"Or use OAuth client credentials:
export CUA_CLIENT_ID="<your-client-id>"
export CUA_CLIENT_SECRET="<your-client-secret>"Keep credentials in your shell environment or a secret manager. Do not put them in the image definition or commit them to source control.
Choose a globally unique, lowercase DNS-label pool name:
export CUA_POOL_NAME="<unique-lowercase-name>"Provision and claim an Omarchy desktop#
Save this script as run_omarchy_fleet.py:
# /// script
# requires-python = ">=3.11,<3.14"
# dependencies = [
# "cua-sandbox==0.7.0",
# ]
# ///
import asyncio
import os
from pathlib import Path
from cua_sandbox import Image, Pool
IMAGE = os.environ.get(
"OMARCHY_IMAGE",
"public.ecr.aws/k5j5w0x5/cua-omarchy-workspace"
"@sha256:dc448dcc986f443980dfb3586f1b91d8699091ad96a74b369016535f0dbb0342",
)
POOL_NAME = os.environ["CUA_POOL_NAME"]
async def main() -> None:
image = Image.from_registry(IMAGE, os_type="linux", kind="vm")
pool = await Pool.apply(
image,
name=POOL_NAME,
replicas=1,
cpu=4,
memory_mb=6144,
services={"server": 8000, "mcp": 3000},
ttl_seconds_after_created=21600,
)
try:
async with pool.claim(service="server", time_to_start=1800) as sandbox:
print(f"Sandbox: {sandbox.name}")
print(f"Screen: {await sandbox.get_dimensions()}")
result = await sandbox.shell.run("pgrep -a Hyprland")
if not result.success:
raise RuntimeError(result.stderr)
print(result.stdout.strip())
screenshot = Path("omarchy-fleet.png")
screenshot.write_bytes(await sandbox.screenshot())
print(f"Screenshot: {screenshot.resolve()}")
await sandbox.clipboard.set("hello from Omarchy Fleet")
if await sandbox.clipboard.get() != "hello from Omarchy Fleet":
raise RuntimeError("clipboard round trip failed")
width, height = await sandbox.get_dimensions()
await sandbox.mouse.click(width // 2, height // 2)
await sandbox.keyboard.keypress(["cmd", "2"])
print("Screenshot, shell, clipboard, click, and workspace hotkey passed")
finally:
await pool.delete()
asyncio.run(main())Run the script:
uv run run_omarchy_fleet.pyPool.apply() creates or reconciles the named pool and its template. The
server service on port 8000 carries screenshot, shell, keyboard, mouse, and
clipboard operations. The mcp service on port 3000 carries the Cua Driver
MCP endpoint at /mcp for an MCP client.
The pool name is globally unique across Cua accounts. If the name is already in use, choose another name and run the script again:
export CUA_POOL_NAME="my-team-omarchy-pool"
uv run run_omarchy_fleet.pyView the desktop#
Use sandbox.screenshot() to inspect the desktop and the mouse, keyboard,
shell, clipboard, and window interfaces to control it without a live display.
For an interactive browser display, expose a Fleet service named vnc and
configure the guest's existing WayVNC process with a noVNC bridge. Follow
Connect to an Omarchy Fleet desktop with
noVNC. Fleet carries the
display through its authenticated HTTP and WebSocket service proxy; it does
not expose a public raw VNC TCP socket.
Connect an MCP client#
The claim exposes the Cua Driver MCP service as the named Fleet service
mcp. Use the SDK service interface while the claim is active:
response = await sandbox.services.request(
"mcp",
method="POST",
path="/mcp",
headers={
"content-type": "application/json",
"accept": "application/json, text/event-stream",
},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": {"name": "my-agent", "version": "0.1.0"},
},
},
)
response.raise_for_status()For a complete MCP client, use the Cua Driver MCP tool reference and keep the authenticated Fleet claim alive for the duration of the client session.
Keep or delete the pool#
The example sets a six-hour creation-age TTL and deletes the pool in finally.
That is a good default for jobs and CI. If you want a reusable warm pool, omit
the finally deletion and release only the claim; delete the pool explicitly
when you are finished:
await pool.delete()Deleting the pool removes its template and sandboxes. Save screenshots or files that you need before the claim and pool are deleted.
Troubleshoot startup#
- HTTP 403 during
Pool.apply(): confirm the image repository is included in Fleet admission policy and that your credentials can create a pool in the selected namespace. - Claim timeout: verify that the image is an amd64 KubeVirt containerDisk,
the image reference includes the exact digest, and the
serverservice is configured on port8000. - Black or empty screenshot: check the image's unattended Hyprland boot and
the
cua-computer-serverservice before debugging the Fleet transport. - MCP connection failure: claim the sandbox with the
mcpservice exposed on port3000and send requests to/mcpthroughsandbox.services.
For local Omarchy development and ARM64 compatibility notes, see Run Omarchy on Apple Silicon.