Workbenches is an open standard. Read why
Docs Runtimes

Running Workbenches

Runtimes

A runtime is where a run executes. A Workbench declares the runtimes it supports in its manifest; the first is the default. Choose another with --runtime on run, smoke, build, and create. A resumed session keeps the runtime it started on.

wb view project-core                        # lists every declared runtime
wb smoke project-core --runtime docker
wb run project-core --runtime e2b --task "Review this migration"
Runtime Needs Edits
local The runner and tools on the host. Land in your working tree.
docker A Docker-compatible CLI and daemon. The image holds the runner and tools. Land in the mounted workspace.
e2b An E2B API key. The image holds the runner, tools, git, and GNU tar. Return as a pending outcome.
daytona A Daytona API key and a published image with the runner, tools, git, and GNU tar. Return as a pending outcome.

Requirements#

Before anything is prepared, the Workbench's requirements are checked against the selected runtime. A requirement a runtime cannot verify is reported as unchecked, never as satisfied.

Runtime How requirements are checked
local Compares os, arch, CPU count, and total memory with the host. disk_gb is not checked. A GPU requirement passes when nvidia-smi --query-gpu=name,memory.total (Linux) or system_profiler SPDisplaysDataType (macOS) lists a GPU.
docker Needs linux in os or no os, and the host architecture in arch. Applies cpu and memory_gb as container limits. Runs a GPU requirement only when docker info lists the NVIDIA container runtime, and passes --gpus all. Never on macOS.
e2b Needs linux in os or no os. Refuses gpu: true. Does not enforce arch, cpu, memory_gb, or disk_gb; smoke reports them as unchecked.
daytona The class must match os. Runs only the linux class and refuses gpu: true. Applies cpu, memory_gb, and disk_gb as the sandbox's resources, defaulting to 2 vCPUs and 4 GiB when not declared. Reports arch as unchecked.

When local GPU discovery finds nothing, the run is refused with the reason, such as nvidia-smi is not installed. Pass --allow-unchecked-gpu to run, smoke, or create to run anyway on a GPU discovery cannot see.

local#

The run executes directly on the host, in the selected directory. Declared tools must be on the host's PATH. Declared workspace access is checked before launch, but local execution is not an operating-system sandbox: the run can reach anything your account can, including credentials already on the machine.

The runner uses its normal local sign-in. See Models and connections.

docker#

The run executes in the Workbench's declared image: a published OCI image, or a package-local Dockerfile.

runtimes:
    docker:
        image:
            build: ./Dockerfile.workbench
            context: .

run pulls or builds the image automatically; wb build does it ahead of time. Published tags run by their resolved digest.

  • The workspace is mounted read-write at /workspace and the package read-only. Named workspaces mount at /workspaces/<name> with their declared access.
  • A Workbench that declares a variable the Docker CLI reads itself (DOCKER_*, BUILDKIT_*, BUILDX_*, COMPOSE_*, XDG_*, HOME, PATH, TMPDIR) is refused: A Docker run cannot pass NAME to the container: the Docker CLI reads that name itself.
  • The container's root filesystem is read-only and /tmp is writable. Images must tolerate that and write caches beneath the provided temporary HOME.
  • Where the host exposes a numeric user and group, the container runs as that identity so files keep host ownership.
  • Runner credentials that Workbench manages live in a private per-runner named volume.
  • Multi-turn sessions keep the runner in the container. A runner's HTTP service is published only on a dynamically assigned host loopback port.

Docker Desktop mediates bind mounts through its virtual machine, so performance and permissions can differ from native Linux. A git worktree does not work as a Docker workspace, because its .git file points at a parent repository that is not mounted: use a full clone. Containers and the host also conflict over node_modules built for different platforms, so give a containerized run its own clone.

Host Docker access#

A Workbench that must build or run sibling containers declares it:

runtimes:
    docker:
        image: ghcr.io/example/project-workbench:0.4.0
        docker:
            engine:
                mode: host

The declaration is not authorization. Every smoke, run, resume, and create needs an explicit grant:

wb run project-core --allow-host-docker --task "Start the local stack"

Without it: Host Docker engine access requires explicit --allow-host-docker authorization. Host Docker access is effectively administrative access to the Docker host and can escape the container's isolation. The image must contain the Docker CLI. Host-engine runs keep host workspace paths inside the container so nested bind mounts resolve.

e2b#

The run executes in a fresh E2B sandbox created from a reusable template built from the Workbench's image.

wb connect --runtime e2b
wb build project-core
wb smoke project-core
wb run project-core --runtime e2b --task "Review this migration"

The key comes from E2B_API_KEY, or from wb connect --runtime e2b, which saves it on the host. An inherited E2B_API_KEY overrides the saved key. The key stays on the host and never enters the sandbox.

  • The image can be a public OCI reference or a package-local Dockerfile. Use versioned tags or digests, because an existing template is reused.
  • The image must include the runner, every declared tool, git, and GNU tar with --null support.
  • The workspace is staged at /workspace, named workspaces at /workspaces/<name>, and the package at /workbench.
  • Gitignored files, repository metadata, dependency trees, common credential stores, and secret-bearing files such as .env are not uploaded.
  • Changes return as pending changesets. Apply them with wb outcome <id> --apply.
  • Input and output transfers each have a 512 MiB limit.
  • A resumed session runs in a fresh sandbox after its native session state and credentials are copied in.
  • Sandboxes have a 60 minute provider timeout, which pauses a crash survivor.
  • Terminal events include E2B duration, resources, and a marked infrastructure cost estimate.

wb smoke on E2B creates a real, billable sandbox.

daytona#

The run executes in a fresh Daytona sandbox created from the published image on the Workbench's Daytona entry.

runtimes:
    daytona:
        class: linux
        image: ghcr.io/example/project-workbench:0.4.0
wb connect --runtime daytona
wb smoke project-core --runtime daytona
wb run project-core --runtime daytona --task "Review this migration"

The key comes from DAYTONA_API_KEY, or from wb connect --runtime daytona. An inherited DAYTONA_API_KEY overrides the saved key. DAYTONA_API_URL selects a Daytona API endpoint other than the public one. The key is never printed and never passed to the sandbox.

  • Only the linux class runs. windows, macos, and gpu fail with class X is not available yet.
  • The image must be published. A package-local Dockerfile is refused; there is no fallback to another runtime's image. Without one: The daytona runtime needs an image. Set runtimes.daytona.image.
  • The image must include the runner, every declared tool, git, and GNU tar with --null support.
  • OpenCode, Pi, and Claude Code all run. OpenCode uses its HTTP service; Pi and Claude Code use piped standard input and streamed toolbox logs. Claude Code requires version 2.1.318 or newer in the image.
  • Model credentials come from the environment or --env-file. There is no credential store and no in-run sign-in.
  • Staging, exclusions, the 512 MiB transfer limits, and pending outcomes match E2B.
  • The sandbox has a 60 minute lifetime. The engine sets Daytona's wall-clock TTL, so it is destroyed even if the CLI dies. It carries the labels dev.workbenches.managed, dev.workbenches.run, and dev.workbenches.scope.
  • No outcome recovery and no cost estimate.

wb smoke on Daytona creates a real, billable sandbox.

Save a runtime key#

wb connect --runtime e2b              # masked prompt
wb connect --runtime e2b --stdin      # for automation
wb connect --runtime e2b --status
wb connect --runtime e2b --remove

Daytona takes the same commands with --runtime daytona. There is no flag that takes the key as a value, so it never enters shell history. Both keys are stored in ~/.workbench/runtime.secrets.json with mode 0600, separately from model credentials. Saving a key creates no sandbox and costs nothing.