| title | Cloud Hypervisor architecture |
|---|---|
| description | Architecture, security boundaries, artifacts, networking, lifecycle, CI, and troubleshooting for the Cloud Hypervisor microVM preview. |
Cloud Hypervisor runs the primary agent in a hardware-isolated microVM while AWF keeps Squid and the API proxy in Docker Compose on the host.
:::caution[Preview support]
This runtime requires both --container-runtime cloud-hypervisor and
--cloud-hypervisor-preview. It supports only GitHub-hosted Ubuntu x86_64
runners with KVM and fails closed on other hosts.
:::
The runtime separates infrastructure from workload execution:
GitHub-hosted Ubuntu x86_64 runner
├── Docker Compose
│ ├── Squid proxy
│ └── API proxy
├── AWF control process
│ ├── artifact and host preflight
│ ├── Cloud Hypervisor REST client
│ ├── network namespace and nftables policy
│ ├── cgroup v2 resource limits
│ └── sandboxed virtiofsd processes
└── Cloud Hypervisor microVM
├── pinned Linux kernel and rootfs
├── shared AWF guest supervisor
├── /workspace through virtio-fs
└── agent command
Cloud Hypervisor exposes its lifecycle API over a Unix domain socket. AWF uses
the /api/v1 endpoints needed to create, boot, inspect, shut down, and delete a
VM. Every API request has a bounded timeout and response-size limit.
The implementation is divided into focused modules:
src/cloud-hypervisor-runtime-backend.tsimplements the external agent runtime contract, builds the credential-safe guest environment, probes Squid and the API proxy, executes the command, and preserves diagnostics.src/cloud-hypervisor/manager.tsorchestrates preflight, networking, rootfs preparation, VMM startup, virtio-fs, guest execution, and cleanup.src/cloud-hypervisor/api-client.tsimplements the REST client over the Unix socket.src/cloud-hypervisor/launcher.tsbuilds the non-shell VMM command and computes Landlock rules.src/cloud-hypervisor/vm-config-builder.tsconstructs thevm.createpayload.src/microvm/contains shared network, workspace, VSOCK, guest-protocol, and artifact primitives.guest/microvm-supervisor/contains the shared guest supervisor.guest/cloud-hypervisor/contains Cloud Hypervisor artifact build and verification tooling.
AWF performs these steps for each run:
- Validate the runtime flags, security mode, topology, host eligibility, and required artifact paths and digests.
- Verify Linux, x86_64, KVM, cgroup v2, Landlock, Docker, and required host tools.
- Start the Squid and API proxy infrastructure through Docker Compose.
- Create a dedicated network namespace, veth pair, TAP device, and nftables policy.
- Copy the rootfs, inject the guest supervisor, and stage files in a private run directory.
- Create a bounded cgroup v2 leaf and launch Cloud Hypervisor as the invoking non-root identity.
- Start one sandboxed
virtiofsdprocess for each validated export. - Create and boot the VM, connect to the guest supervisor over VSOCK, and probe infrastructure connectivity.
- Execute the agent command and propagate its exit code. Timeouts return
124. - Sync and unmount guest filesystems, stop the VM and VMM, reap
virtiofsd, and remove network, cgroup, and run-directory resources.
Cleanup is idempotent and aggregates errors so one cleanup failure does not skip later cleanup steps.
The runtime accepts only GitHub-hosted Ubuntu x86_64 runners. Preflight verifies
the GitHub Actions environment markers, /dev/kvm, the KVM group, cgroup v2,
Landlock, and required tools before creating the VM.
AWF never downloads runtime artifacts automatically. You must provide the
Cloud Hypervisor binary, guest kernel, rootfs, supervisor, and virtiofsd
paths together with their expected SHA-256 digests. Preflight rejects missing,
mutable, incorrectly owned, or digest-mismatched artifacts.
:::danger[Fail-closed verification] Do not bypass artifact verification. A substituted VMM, kernel, rootfs, supervisor, or filesystem daemon runs inside a trusted part of the boundary. :::
AWF launches Cloud Hypervisor through ip netns exec and setpriv without a
shell. The process:
- runs as the non-root identity recorded by
SUDO_UIDandSUDO_GID; - keeps only the KVM supplementary group;
- sets
no_new_privs; - retains only
CAP_NET_ADMIN, which the virtio-net TAP setup requires; - uses Cloud Hypervisor's seccomp filter;
- receives a minimal Landlock filesystem allowlist; and
- belongs to a cgroup v2 leaf with explicit memory, CPU, and PID limits.
The private run directory is under
/run/awf-cloud-hypervisor/<binary>/<runId>/. Its per-run leaf is accessible
only to the selected non-root identity and root.
The API proxy is mandatory. Provider credentials remain in the host-side proxy and are not copied into the guest environment. The guest receives only the proxy endpoint and non-secret execution settings.
Each run uses deterministic, length-bounded resource names:
- namespace:
awfvm-<token> - host veth:
vmh<token> - namespace veth:
vmn<token> - TAP device:
vmt<token>
The namespace connects the guest TAP to AWF's host-side infrastructure. nftables permits only the required paths to Squid and the API proxy and denies direct internet, arbitrary TCP, direct DNS, and instance metadata access. Guest proxy environment variables improve client compatibility, but the namespace policy is the enforcement boundary.
The guest boots a pinned PCI-capable Linux kernel and deterministic BusyBox
rootfs. AWF injects the binary built from guest/microvm-supervisor/ into the
per-run rootfs.
The workspace is a live read-write virtio-fs export mounted at /workspace.
Validated additional exports use separate sandboxed virtiofsd processes.
The workspace path is never exposed directly to the VMM process through its
Landlock rules.
Temporary microVM workspace data lives under:
<workDir>/microvm-images/<runId>/
With --keep-containers, AWF preserves this directory, the network namespace,
and runtime diagnostics for investigation.
The preview rejects configurations that weaken or conflict with its boundary, including:
- self-hosted, non-Ubuntu, non-x86_64, or non-KVM hosts;
- remote Docker daemons;
- TTY mode;
- Docker-in-Docker agent execution;
- topology peers;
- unsupported host mounts; and
- enclave combinations not supported by the external runtime contract.
Selecting Cloud Hypervisor never falls back to Docker, gVisor, or sbx.
.github/workflows/test-cloud-hypervisor.yml provides deterministic build and
live-KVM jobs.
The build job:
- builds the pinned Cloud Hypervisor binary, Linux kernel, BusyBox rootfs,
shared guest supervisor, and
virtiofsd; - verifies source and output digests;
- attests provenance; and
- uploads the
cloud-hypervisor-test-x86_64workflow artifact.
The live job runs only when explicitly enabled by workflow dispatch or the
cloud-hypervisor-kvm pull-request label. It executes
scripts/ci/cloud-hypervisor-live-smoke.sh, which validates:
- allowed HTTPS and blocked domains;
- direct-egress, arbitrary-TCP, DNS, and metadata denial;
- API proxy reachability and secret non-disclosure;
- workspace persistence;
- exit-code, timeout, and cancellation behavior;
- device assumptions;
- partial-start and normal cleanup;
- preserved-state behavior; and
- uid, capabilities,
no_new_privs, seccomp, cgroup, Landlock, and VM-device security assertions.
After each case, the suite checks for leaked awfvm-* namespaces,
vmh*/vmn*/vmt* interfaces, cgroups, and Cloud Hypervisor processes.
Confirm the job runs on a GitHub-hosted Ubuntu x86_64 runner and that KVM is usable:
uname -m
test -r /dev/kvm && test -w /dev/kvm
stat -c '%A %U %G %n' /dev/kvmThe runtime intentionally rejects self-hosted runners even if they expose KVM.
Run with --keep-containers, then inspect the namespace and interfaces:
sudo ip netns list | grep '^awfvm-'
sudo ip -o link show | grep -E ' (vmh|vmn|vmt)[0-9a-f]{12}[:@]'
sudo nft list rulesetInspect preserved workspace data under
<workDir>/microvm-images/<runId>/ and VMM diagnostics under the run's
preserved log directory.
:::caution Preserved namespaces and processes continue consuming host resources. Remove them only after collecting the diagnostics you need. :::
Check the namespace nftables rules, TAP state, and Squid/API proxy health. The guest must not have a direct route to the internet; fixing connectivity by loosening the default-deny policy would break the security boundary.
Verify the launcher retained only CAP_NET_ADMIN, the TAP belongs to the
expected namespace, and the Landlock allowlist includes the TAP's
/sys/class/net/<tapName> directory read-only.