Sandbox Compute Drivers

View as Markdown

The gateway’s configured compute driver determines how OpenShell creates each sandbox. The CLI workflow stays the same across drivers: you create, connect to, inspect, and delete sandboxes through the gateway API.

Every compute driver runs the OpenShell supervisor inside the sandbox workload. The supervisor launches the agent process, applies policy, routes egress through the proxy, injects configured credentials, and maintains the gateway session.

Configure a Compute Driver

Configure the compute driver on the gateway. Current releases accept one driver per gateway. Set compute_drivers in the gateway TOML file:

1[openshell.gateway]
2compute_drivers = ["docker"]

Supported values are docker, podman, kubernetes, and vm.

When compute_drivers is unset, the gateway auto-detects Kubernetes, then Podman, then Docker by CLI availability or a local Unix socket. The VM driver is never auto-detected; configure it explicitly with compute_drivers = ["vm"] or set OPENSHELL_DRIVERS=vm in the launch environment.

Common gateway options:

Gateway TOML optionDescription
compute_drivers = ["<driver>"]Select the compute driver. Supported values are docker, podman, kubernetes, and vm.

Set driver-specific values such as sandbox images, callback endpoints, network names, TLS material, and VM sizing in the gateway TOML file. See the Gateway Configuration File reference for the full [openshell.drivers.<name>] schema.

Sandbox create supports --cpu and --memory for per-sandbox compute sizing. Docker and Podman apply them as runtime limits. Kubernetes applies them as both container requests and limits. The VM driver accepts the fields but currently ignores them.

Sandbox create also accepts experimental driver-owned config through --driver-config-json. The value is a JSON object keyed by driver name. The gateway forwards only the block for the active driver, so a Kubernetes gateway receives the kubernetes object from a value such as:

Nested keys inside each driver block use snake_case. The top-level envelope keys are driver names, such as kubernetes, and are not part of the nested schema.

$openshell sandbox create \
> --driver-config-json '{"kubernetes":{"pod":{"runtime_class_name":"kata-containers","priority_class_name":"batch-low"}}}' \
> -- claude

Driver config is for fields without a stable public flag. Prefer --cpu, --memory, and --gpu for portable resource intent.

Exact GPU device selection remains driver-owned and requires --gpu. Docker and Podman accept cdi_devices; replace the top-level docker key with podman when using the Podman driver, for example {"docker":{"cdi_devices":["nvidia.com/gpu=0"]}}. The VM driver accepts gpu_device_ids, for example {"vm":{"gpu_device_ids":["0000:2d:00.0"]}}; the current VM implementation accepts at most one entry.

For Kubernetes, pod.runtime_class_name maps to PodSpec runtimeClassName. It overrides the gateway’s configured default runtime class for that sandbox, while a typed SandboxTemplate.runtime_class_name value from the API still takes precedence.

Docker Driver

Docker-backed sandboxes run as containers on the gateway host. Use Docker for local development, single-machine gateways, and hosts that already use Docker Desktop or Docker Engine.

The gateway talks to the Docker daemon to create sandbox containers. Docker is also required for local image builds from directories or Dockerfiles.

For maintainer-level implementation details, refer to the Docker driver README.

Select Docker with compute_drivers = ["docker"] in [openshell.gateway]. Configure Docker driver values such as grpc_endpoint, network_name, supervisor_bin, supervisor_image, image_pull_policy, ssh_socket_path, sandbox_pids_limit, and guest_tls_* in [openshell.drivers.docker].

For GPU-backed Docker sandboxes, configure Docker CDI before starting the gateway so OpenShell can detect the daemon capability.

Docker Driver Config Mounts

Docker driver config accepts user-supplied volume and tmpfs mounts. It also accepts bind mounts when [openshell.drivers.docker] sets enable_bind_mounts = true in gateway.toml. See Docker’s storage documentation for more information. Docker local-driver named volumes created with bind options also expose gateway-host paths, so OpenShell treats them like bind mounts and requires enable_bind_mounts = true.

Use a volume mount for existing Docker named volumes:

$docker volume create openshell-work
$
$openshell sandbox create \
> --driver-config-json '{"docker":{"mounts":[{"type":"volume","source":"openshell-work","target":"/sandbox/work","read_only":false}]}}' \
> -- claude

Bind mounts share gateway-host filesystem resources with the sandbox. They may be considered insecure because they can negate OpenShell controls such as workspace isolation and filesystem policy. Use them only when you understand and accept that loss of isolation.

Use a bind mount only after enabling it in the Docker driver table:

1[openshell.drivers.docker]
2enable_bind_mounts = true
$openshell sandbox create \
> --driver-config-json '{"docker":{"mounts":[{"type":"bind","source":"/srv/openshell/work","target":"/sandbox/work","read_only":false}]}}' \
> -- claude

Docker mount schema:

TypeFields
bindsource, target, optional read_only (true by default). source must be an absolute host path. Requires enable_bind_mounts = true.
volumesource, target, optional read_only (true by default), optional subpath. The named volume must already exist. Docker local-driver bind-backed volumes require enable_bind_mounts = true.
tmpfstarget, optional options, optional size_bytes, optional mode.

OpenShell rejects mount targets that replace the workspace root, container root, supervisor files, /etc/openshell, /etc/openshell-tls, authentication material, or network namespace paths. These checks do not make host bind mounts safe.

Podman Driver

Podman-backed sandboxes run as rootless containers on the gateway host. Use Podman for Linux workstation workflows that avoid a rootful Docker daemon.

The gateway talks to the Podman API socket. The Podman driver requires Podman 5.x, cgroups v2, rootless networking, and an active Podman user socket.

For maintainer-level implementation details, refer to the Podman driver README and Podman networking notes.

Select Podman with compute_drivers = ["podman"] in [openshell.gateway]. Configure Podman driver values such as socket_path, network_name, supervisor_image, stop_timeout_secs, image_pull_policy, grpc_endpoint, host_gateway_ip, sandbox_ssh_socket_path, sandbox_pids_limit, and guest_tls_* in [openshell.drivers.podman].

On macOS with podman machine, the driver uses gvproxy’s host-loopback IP, 192.168.127.254, for sandbox host aliases by default. Set host_gateway_ip only when your Podman machine uses a non-standard host-loopback address. On Linux, an empty host_gateway_ip keeps Podman’s host-gateway resolver behavior.

Podman Driver Config Mounts

Podman driver config accepts user-supplied volume, tmpfs, and image mounts. It also accepts bind mounts when [openshell.drivers.podman] sets enable_bind_mounts = true in gateway.toml. Podman local-driver named volumes created with bind options also expose gateway-host paths, so OpenShell treats them like bind mounts and requires enable_bind_mounts = true. Host bind mounts expose gateway host paths to sandbox requests, so they are disabled by default.

Use a volume mount for existing Podman named volumes:

$podman volume create openshell-work
$
$openshell sandbox create \
> --driver-config-json '{"podman":{"mounts":[{"type":"volume","source":"openshell-work","target":"/sandbox/work","read_only":false}]}}' \
> -- claude

Bind mounts share gateway-host filesystem resources with the sandbox. They may be considered insecure because they can negate OpenShell controls such as workspace isolation and filesystem policy. Use them only when you understand and accept that loss of isolation.

Use a bind mount only after enabling it in the Podman driver table:

1[openshell.drivers.podman]
2enable_bind_mounts = true
$openshell sandbox create \
> --driver-config-json '{"podman":{"mounts":[{"type":"bind","source":"/srv/openshell/work","target":"/sandbox/work","read_only":false}]}}' \
> -- claude

Podman mount schema:

TypeFields
bindsource, target, optional read_only (true by default). source must be an absolute host path. Requires enable_bind_mounts = true.
volumesource, target, optional read_only (true by default). The named volume must already exist. Podman local-driver bind-backed volumes require enable_bind_mounts = true.
tmpfstarget, optional options, optional size_bytes, optional mode.
imagesource, target, optional read_only (true by default).

Podman volume and image mounts do not support subpath in OpenShell driver config. OpenShell rejects mount targets that replace the workspace root, container root, supervisor files, /etc/openshell, /etc/openshell-tls, authentication material, or network namespace paths. These checks do not make host bind mounts safe.

MicroVM Driver

MicroVM-backed sandboxes run inside VM-backed isolation instead of a container boundary. Use MicroVM when workloads need a VM boundary instead of a local container boundary.

The gateway uses the VM compute driver to create VM-backed sandboxes. MicroVM requires host virtualization support. It uses libkrun with Apple’s Hypervisor framework on macOS, KVM on Linux, and QEMU for GPU-backed sandboxes on Linux.

The VM driver boots a cached immutable bootstrap ext4 root disk. When the requested sandbox image differs from the bootstrap image, the driver stages the registry image as an OCI layout, unpacks it inside a bootstrap VM with umoci, and caches the prepared image disk by image identity. Each sandbox receives that prepared disk read-only plus its own writable overlay.ext4 disk for /, including /sandbox writes and runtime TLS material. The overlay persists for the sandbox lifetime and is deleted with the sandbox state directory.

VM sandbox creation follows the same progress model as Kubernetes-backed sandboxes. The gateway accepts the sandbox, then the VM driver publishes watch events while it resolves the image, prepares or reuses the bootstrap and prepared image caches, creates the writable overlay, and starts the VM launcher.

On gateway restart, the gateway starts a fresh VM driver process. The driver scans its state directory for accepted sandbox launch records, restarts those VMs, and reuses each sandbox’s existing overlay.ext4 so files written inside the sandbox remain available after the supervisor reconnects.

For maintainer-level implementation details, refer to the VM driver README.

Enable the VM Driver

The VM driver is opt-in. Release packages can install openshell-driver-vm, but the gateway does not select it unless you configure the driver explicitly.

Enable VM by setting compute_drivers = ["vm"] in the gateway TOML file:

1[openshell.gateway]
2compute_drivers = ["vm"]

For a launch-time override, set OPENSHELL_DRIVERS=vm in the gateway environment and restart the service.

Configure VM driver values such as grpc_endpoint, driver_dir, state_dir, default_image, bootstrap_image, vcpus, mem_mib, overlay_disk_mib, krun_log_level, and guest_tls_* in [openshell.drivers.vm]. The VM state_dir stores overlay disks, console logs, runtime state, image-rootfs cache, and the private run/compute-driver.sock socket.

The gateway starts openshell-driver-vm over a private Unix socket and passes its process ID so the driver can reject unexpected local clients. The driver’s standalone TCP listener is disabled unless --allow-unauthenticated-tcp is set for local development.

Local image resolution

The VM driver resolves sandbox images from a local container engine before falling back to registry pulls. It tries Docker first, then falls back to the Podman socket (Docker-compatible API). On Linux with Podman, enable the API socket so the driver can find local images:

$systemctl --user start podman.socket

Host Firewall

The VM driver creates nftables rules on the host for each sandbox VM’s TAP network interface. These rules provide NAT for VM connectivity and defense-in-depth isolation: unsolicited inbound connections to the VM are dropped, and the VM can only reach the gateway port on the host. Primary security enforcement (proxy-only egress and bypass detection) is handled by the sandbox supervisor inside the VM guest.

On hosts with restrictive firewalls (e.g. firewalld), the host firewall may additionally block VM traffic that the driver’s rules accept. If VM sandboxes cannot reach the network, verify that the host firewall allows forwarding and input for vmtap-* interfaces. See the VM driver README for details.

Kubernetes Driver

Kubernetes-backed sandboxes run as pods in the configured sandbox namespace. Use Kubernetes for shared clusters, remote compute, GPU scheduling, and operator-managed environments.

Helm deployments set Kubernetes driver values through the chart.

For maintainer-level implementation details, refer to the Kubernetes driver README.

Gateway configurationHelm valueDescription
compute_drivers = ["kubernetes"]Not applicableSelect the Kubernetes compute driver.
[openshell.drivers.kubernetes].namespaceserver.sandboxNamespaceSet the namespace for sandbox resources. The Helm chart defaults to the release namespace when left empty.
service_account_namesandboxServiceAccount.nameSet the Kubernetes service account assigned to sandbox pods and accepted by the gateway TokenReview bootstrap path. The Helm chart creates a dedicated sandbox service account by default.
default_imageserver.sandboxImageSet the default sandbox image.
image_pull_policyserver.sandboxImagePullPolicySet the Kubernetes image pull policy for sandbox pods.
image_pull_secretsserver.sandboxImagePullSecretsAttach Kubernetes image pull secrets to sandbox pods. Referenced Secrets must exist in the sandbox namespace.
grpc_endpointserver.grpcEndpointSet the gateway callback endpoint reachable from sandbox pods.
client_tls_secret_nameserver.tls.clientTlsSecretNameMount sandbox client TLS materials from a Kubernetes secret.
supervisor_imagesupervisor.image.repository / supervisor.image.tagSet the supervisor image that provides the openshell-sandbox binary.
supervisor_image_pull_policysupervisor.image.pullPolicySet the Kubernetes image pull policy for the supervisor image.
supervisor_sideload_methodsupervisor.sideloadMethodHow the supervisor binary is delivered into sandbox pods. Leave empty to auto-detect from cluster version. Set to image-volume to mount the supervisor OCI image directly as a volume (requires Kubernetes 1.33+ with the ImageVolume feature gate; GA in 1.36), or init-container to copy it through an init container on older clusters.
app_armor_profileserver.appArmorProfileSet the sandbox agent container’s AppArmor profile. Helm defaults this to Unconfined so AppArmor-enabled nodes do not block supervisor network namespace setup. Set the Helm value to an empty string to omit the field, or use RuntimeDefault or Localhost/<profile-name> for operator-managed profiles.
workspace_default_storage_sizeserver.workspaceDefaultStorageSizeSet the default workspace PVC size for new sandboxes.
sa_token_ttl_secsserver.sandboxJwt.k8sSaTokenTtlSecsSet the projected ServiceAccount token TTL used for the bootstrap token exchange.

The Kubernetes driver creates namespaced agents.x-k8s.io/v1alpha1 Sandbox resources from the Kubernetes SIG Apps agent-sandbox project. The Agent Sandbox controller turns those resources into sandbox pods and related storage.

Sandbox.spec.volumeClaimTemplates is immutable after creation. To change storage configuration, delete the sandbox and create a new one with the updated spec.