# How to Execute Code and Commands Using `workspace.runtime.exec` in the Cloudflare Computer SDK

> Learn to execute code and commands with workspace runtime exec in the Cloudflare Computer SDK. Gain control over process lifecycle, stream output, and retrieve results.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: how-to-guide
- Published: 2026-09-04

---

**The `workspace.runtime.exec` method is the primary entry point for running arbitrary shell commands inside a Cloudflare Computer workspace, returning an `ExecHandle` that provides methods to retrieve results, stream output, and manage the process lifecycle.**

The `workspace.runtime.exec` API enables developers to execute arbitrary commands within a Computer workspace environment. Defined in [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts) at line 10, this method serves as the main interface to the runtime execution layer. When invoked, it constructs a `RuntimeExecRequest` envelope and forwards it via RPC to the appropriate backend infrastructure.

## Understanding the Runtime Execution Architecture

The execution flow begins with the **Workspace** class, where `runtime.exec` accepts a command string and optional configuration object. According to the Cloudflare Computer source code, this method immediately delegates to the runtime layer defined in [`packages/computer/src/runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts). The runtime builds the execution envelope and dispatches it across the RPC channel to the target backend—whether that be the worker-shell, a command-line process, or a stub implementation for testing.

The method returns a **Promise** that resolves to an **ExecHandle** object. This handle represents the live execution state and remains valid for the duration of the process lifecycle, allowing asynchronous interaction with running commands.

## The ExecHandle Interface

Once you obtain an `ExecHandle` from `workspace.runtime.exec`, you gain access to several critical methods for process management:

- **`result()`**: Resolves when the command completes, returning an object containing `code`, `stdout`, and `stderr`
- **`stream()`**: Returns an async iterator yielding JSON-L events for real-time output monitoring
- **`kill()`**: Terminates the running process (sends SIGKILL)
- **`id`**: Property containing the unique execution identifier

Per the implementation in [`packages/computer/src/runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts) at line 64, the `stream()` method emits structured events including stdout data, stderr data, and exit notifications.

## Executing Commands: Practical Examples

### Basic Command Execution

To run a simple command and retrieve its exit status, invoke `exec` with the command string and await the result:

```typescript
const exec = await workspace.runtime.exec("true");
const result = await exec.result();
// result → { code: 0, stdout: "", stderr: "" }

```

### Capturing UTF-8 Output

Specify the `encoding` option to automatically decode bytes as UTF-8 strings rather than raw buffers:

```typescript
const exec = await workspace.runtime.exec("printf 'hello world'", {
  encoding: "utf8",
});
const { stdout } = await exec.result();
// stdout === "hello world"

```

### Piping Input to Commands

Send data to a process's standard input using the `stdin` option:

```typescript
const exec = await workspace.runtime.exec("cat", {
  stdin: "piped input",
  encoding: "utf8",
});
const { stdout } = await exec.result();
// stdout === "piped input"

```

### Streaming Real-Time Output

For long-running commands, consume output incrementally via the `stream()` method rather than waiting for completion:

```typescript
const exec = await workspace.runtime.exec("yes | head -n 5", {
  encoding: "utf8",
});

for await (const event of exec.stream()) {
  // event format: { type: "stdout", data: "..." }
  if (event.type === "stdout") {
    console.log(event.data);
  }
}

```

### Specifying Execution Backends

Force execution on a specific backend by providing the `backend` option. The routing logic resides in [`packages/computer/src/tools/exec.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/tools/exec.ts) at line 205:

```typescript
await workspace.runtime.exec("echo $CF_WORKER", {
  backend: "worker-shell",  // Routes to worker-shell backend
});

```

If omitted, the system uses the workspace's default backend configuration.

### Canceling Running Processes

Terminate execution programmatically using the `kill()` method:

```typescript
const exec = await workspace.runtime.exec("sleep 30");
await exec.kill();           // Sends SIGKILL signal
await exec.result();         // Promise rejects with termination error

```

### Reusing Executions with IDs

Supply an optional `id` parameter to deduplicate executions or retrieve existing handles. As enforced in [`packages/computer/src/runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts) at line 369, IDs must be non-empty and cannot exceed 256 bytes:

```typescript
// First call creates the execution
await workspace.runtime.exec("true", { id: "my-run" });

// Subsequent calls with identical ID reuse the existing handle
const exec2 = await workspace.runtime.exec("true", { id: "my-run" });

```

## Configuration and Error Handling

### ExecOptions Parameters

The `workspace.runtime.exec` method accepts an **ExecOptions** object supporting:
- **`backend`**: Target execution environment (e.g., "worker-shell")
- **`stdin`**: String or buffer to pipe into the process
- **`encoding`**: Set to `"utf8"` for automatic string decoding
- **`sync`**: Boolean for synchronous execution mode
- **`id`**: String identifier for execution reuse (max 256 bytes)

### Backend Routing

When the `backend` option is specified, the request routes through the logic in [`packages/computer/src/tools/exec.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/tools/exec.ts). If the specified backend is unavailable, the method rejects with a "no execution backend" error. The test suite at [`packages/computer/src/workspace.test.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.test.ts) line 891 validates this error condition.

### Error Handling and Timeouts

Unconfigured or missing backends trigger immediate promise rejection. For timeout management, implement logic around the `ExecHandle` or use shell-level timeout commands, as the core method focuses on execution dispatch rather than timeout enforcement.

## Observability and Tracing

Every `workspace.runtime.exec` call automatically generates OpenTelemetry-compatible tracing spans. As implemented in [`packages/computer/src/observe.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/observe.ts) at line 10, the system creates:
- A parent span named `workspace.runtime.exec`
- A child span named `workspace.runtime.exec.spawn`

These spans integrate with Cloudflare's tracing infrastructure, providing visibility into command execution latency and backend selection.

## Summary

- **`workspace.runtime.exec`** (defined in [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts)) is the primary method for command execution in Computer workspaces
- Returns an **ExecHandle** offering `result()`, `stream()`, and `kill()` methods for process interaction
- Supports **ExecOptions** including `backend` routing, `stdin` piping, `encoding` selection, and execution `id` reuse
- Routes requests through the runtime layer ([`packages/computer/src/runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts)) to appropriate backends via RPC
- Automatically generates **OpenTelemetry spans** (`workspace.runtime.exec`) for observability integration
- Enforces ID length constraints (≤256 bytes) and validates backend availability before execution

## Frequently Asked Questions

### How do I capture both stdout and stderr from a command?

Call `await exec.result()` on the **ExecHandle** returned by `workspace.runtime.exec`. The resolved object contains `stdout`, `stderr`, and `code` properties. When using `encoding: "utf8"`, both streams return as decoded strings; otherwise, they return as raw buffers.

### Can I execute commands synchronously using workspace.runtime.exec?

Yes, set the `sync: true` option in the **ExecOptions** object. However, the method still returns a Promise that resolves to an `ExecHandle`. The synchronous flag primarily affects how the runtime processes the execution request rather than making the JavaScript call blocking.

### What happens if I specify an execution ID that already exists?

The runtime returns the existing **ExecHandle** for that ID rather than spawning a new process. As specified in [`packages/computer/src/runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts), the ID must be unique per workspace and no longer than 256 bytes. This mechanism enables deduplication and allows multiple parts of your application to reference the same running process.

### How does backend routing work if I don't specify a backend?

If the `backend` option is omitted, `workspace.runtime.exec` uses the workspace's default backend configuration. The routing logic in [`packages/computer/src/tools/exec.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/tools/exec.ts) dispatches to this default, which typically resolves to the worker-shell or the environment's primary execution context.