Cuttlefish Driver

jumpstarter-driver-cuttlefish manages Android Cuttlefish virtual devices through the Host Orchestrator REST API. It provides full CVD (Cuttlefish Virtual Device) lifecycle management through standard Jumpstarter interfaces: VirtualPowerInterface for on/off/cycle, plus cuttlefish-specific operations (snapshot, powerwash, restart).

Installation

$ pip3 install --extra-index-url https://pkg.jumpstarter.dev/simple jumpstarter-driver-cuttlefish

Prerequisites

  • A running Cuttlefish Host Orchestrator (port 2080 by default)

Host Setup

A cvd-images named volume mounted at /home/vsoc-01/fetch persists fetched AOSP images across container restarts. Instance state (/var/tmp/cvd) is deliberately kept ephemeral — restarting the container gives you a clean slate with no orphaned instance directories.

# 1. Pull the orchestration image
podman pull us-docker.pkg.dev/android-cuttlefish-artifacts/cuttlefish-orchestration/cuttlefish-orchestration:stable

# 2. Create a named volume for AOSP images
podman volume create cvd-images

# 3. Start the container
#    --network=host: netsim and rootcanal bind to 127.0.0.1 inside the
#    container, so without host networking they'd be unreachable from
#    outside. Host networking shares the VM's network namespace directly.
#
#    Security note: --privileged + --network=host gives the container full
#    access to the VM's network stack. HO, netsim, and rootcanal have no
#    auth — only deploy on dedicated, non-public hosts.
podman run -d \
  --name cuttlefish-orchestrator \
  --restart=always \
  --privileged \
  --network=host \
  -v cvd-images:/home/vsoc-01/fetch:Z \
  -v /opt/cuttlefish:/opt/cuttlefish:Z \
  us-docker.pkg.dev/android-cuttlefish-artifacts/cuttlefish-orchestration/cuttlefish-orchestration:stable

# 4. Fix permissions
podman exec cuttlefish-orchestrator chown -R httpcvd:httpcvd /home/vsoc-01/fetch

# 5. Fetch AOSP images (one-time, ~2 minutes)
podman exec cuttlefish-orchestrator cvd fetch \
  --default_build=aosp-android-latest-release/aosp_cf_x86_64_auto-userdebug \
  --target_directory=/home/vsoc-01/fetch

# 6. Verify
curl -s http://localhost:2080/_debug/statusz  # should return 200
curl -s http://localhost:2080/cvds             # should return {"cvds":[]}

Ports

After a CVD boots, the following ports are available on the host. All per-instance ports use the same offset: base + instance_num - 1.

Service

Base port

Instance 1

Instance 2

Host Orchestrator

2080

2080 (fixed)

2080 (fixed)

ADB

6520

6520

6521

Netsim REST

7681

7681

7682

Rootcanal HCI

7300

7300

7301

When using instance_num > 1, update the netsim port and bt_peer hci_port in the exporter config to match.

SSH tunnel (local development only)

For local development, when running the exporter on your workstation instead of as a pod, tunnel ports from the VM. This works because --network=host places netsim and rootcanal on the VM’s loopback - the tunnel’s localhost target reaches them directly.

ssh -L 2080:localhost:2080 \
    -L 6520:localhost:6520 \
    -L 7681:localhost:7681 \
    -L 7300:localhost:7300 \
    fedora@<vm-ip> -p 22000 -N

In production, the exporter runs as a pod and the host config points to the cuttlefish VM’s address directly - no tunnel needed.

Resetting stale state

If CVDs get stuck or orphaned, clear stale state inside the container:

podman exec cuttlefish-orchestrator bash -c '
    rm -rf /var/tmp/cvd/[0-9]* /var/tmp/cvd/lock/* /tmp/cf_avd_* /tmp/vsock_*
    chown -R httpcvd:httpcvd /var/tmp/cvd/
'

Or restart the container - ephemeral /var/tmp/cvd means a restart is equivalent to a full reset. Fetched images in the cvd-images volume are preserved.

Teardown

Delete CVDs and snapshots when done to avoid accumulation:

j power off --destroy          # deletes the CVD
j cuttlefish snapshot delete <id>  # remove specific snapshots

Configuration

Example exporter configuration:

export:
  cuttlefish:
    type: jumpstarter_driver_cuttlefish.driver.Cuttlefish
    config:
      host: localhost
      port: 2080
      instance_num: 1
      env_config:
        instances:
          - disk:
              default_build: /home/vsoc-01/fetch
            vm:
              enable_virtiofs: false   # required for snapshot support
        common:
          host_package: /home/vsoc-01/fetch
          gpu_mode: guest_swiftshader  # required for snapshot support
  netsim:
    type: jumpstarter_driver_netsim.driver.Netsim
    config:
      host: localhost
      port: 7681       # 7681 + instance_num - 1
  bt_peer:
    type: jumpstarter_driver_bt_peer.driver.BtPeer
    config:
      hci_host: 127.0.0.1
      hci_port: 7300    # 7300 + instance_num - 1
  power:
    ref: cuttlefish.power
  adb:
    ref: cuttlefish.adb

Configuration Parameters

Parameter

Description

Type

Required

Default

host

Host Orchestrator hostname

str

no

“localhost”

port

Host Orchestrator HTTP port

int

no

2080

group

CVD group name passed to cvd load. HO auto-assigns a different group name (e.g. cvd_1); the driver tracks the assigned name internally.

str

no

“cvd”

name

CVD instance name within the group

str

no

“1”

instance_num

CVD instance number (determines ADB/netsim/HCI ports). Must match HO’s assigned slot. Pinning avoids drift (see env_config example).

int

no

1

adb_server_port

ADB server port on the exporter

int

no

15037

boot_timeout

Seconds to wait for boot on power on

int

no

300

env_config

Default env_config for CVD creation

dict

no

{}

This is a composite driver with three children:

  • powerVirtualPowerInterface: j power on, j power off [--destroy], j power cycle

  • storageFlasherInterface: not yet implemented (planned: HO artifact upload API)

  • adb — ADB server for device communication

The exporter config also typically includes sibling drivers:

  • netsim (jumpstarter-driver-netsim) — virtual radio control (BLE, WiFi, UWB) via netsim REST API

  • bt_peer (jumpstarter-driver-bt-peer) — Bluetooth peer device via bumble + rootcanal HCI

Use ref: entries in the exporter config to expose children at the top level.

Usage

CLI

# Power on (creates CVD if none exists, starts if stopped)
j power on

# Power off (stops CVD, keeps state)
j power off

# Power off and delete CVD entirely
j power off --destroy

# Power cycle
j power cycle

# Health check
j cuttlefish status

# List all CVDs
j cuttlefish list

# Get this CVD's details
j cuttlefish get

# Restart the CVD
j cuttlefish restart

# Factory reset
j cuttlefish powerwash

# Simulate power button press
j cuttlefish powerbtn

# List running operations
j cuttlefish ops

# Snapshot management
# Requires: x86_64 host, enable_virtiofs: false, gpu_mode: guest_swiftshader
j cuttlefish snapshot create --id my-snapshot
j cuttlefish snapshot delete <snapshot_id>

Python API

from jumpstarter.common.utils import serve
from jumpstarter_driver_cuttlefish.driver import Cuttlefish

driver = Cuttlefish(
    host="localhost",
    port=2080,
    env_config={
        "instances": [{"disk": {"default_build": "/home/vsoc-01/fetch"}}],
        "common": {"host_package": "/home/vsoc-01/fetch"},
    },
)
with serve(driver) as client:
    # Check Host Orchestrator is reachable
    print(client.status())  # "OK"

    # Power on (creates CVD from env_config)
    client.power.on()

    # List CVDs
    cvds = client.list_cvds()
    print(cvds)

    # Snapshots
    client.create_snapshot(snapshot_id="baseline")

    # Cleanup
    client.power.off(destroy=True)

Architecture

┌────────────┐     gRPC      ┌────────────────┐    HTTP     ┌──────────────────┐
│ jmp shell  │──────────────►│ Exporter       │────────────►│ Host             │
│ (client)   │               │  ├─ cuttlefish │  :2080      │ Orchestrator     │
│            │               │  │  ├─ power   │             │                  │
│            │               │  │  ├─ storage │             │  cvd create/     │
│            │               │  │  └─ adb     │             │  start/stop      │
│            │               │  ├─ netsim ────│── :7681 ──►│  netsim REST     │
│            │               │  └─ bt_peer ───│── :7300 ──►│  rootcanal HCI   │
└────────────┘               └────────────────┘             └────────┬─────────┘
                                                                     │
                                                                     ▼
                                                            ┌──────────────────┐
                                                            │ Cuttlefish VM    │
                                                            │ (Android guest)  │
                                                            │  ADB :6520       │
                                                            └──────────────────┘

The driver is a thin REST client that translates Jumpstarter driver calls into Host Orchestrator API requests. Long-running operations (create, start, stop, delete) are handled asynchronously - the driver polls the /operations/:wait endpoint until completion or timeout.

power.on() waits for full boot by default (boot_timeout=300). It polls adb connect + adb devices until the device is online, then waits for sys.boot_completed=1. Set boot_timeout: 0 to skip the wait.

CVD Build Sources

The env_config supports two build source formats in disk.default_build:

  • Android CI: @ab/<branch>/<target> - fetches images from Android Build servers. Example: @ab/aosp-android-latest-release/aosp_cf_x86_64_auto-userdebug (AAOS)

  • Local path: /path/to/android/build - uses pre-fetched images on the host.

API Reference

Driver

class jumpstarter_driver_cuttlefish.driver.Cuttlefish

Cuttlefish Host Orchestrator driver for managing Android virtual devices.

Composite driver with children: power, storage, adb.

classmethod client() str

Return full import path of the corresponding driver client class

driver_type: ClassVar[str] = 'composite'

Driver category for observability (e.g. power, storage, network, serial, console, video, composite).

reset_host() str

Forcefully delete all CVDs and clean host state via HO reset endpoint.

Kills orphaned processes, removes stale files, and resets HO tracking.

status() str

Check that nginx and Host Orchestrator are both reachable.

wait_boot(timeout: int = 0) str

Wait for CVD to finish booting. Uses boot_timeout config if timeout=0.

Client

class jumpstarter_driver_cuttlefish.client.CuttlefishClient

Client for controlling Cuttlefish Host Orchestrator with nested children.

status() str

Get current cached status (non-blocking).

Returns None if status monitor not started.