> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runloop.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenCode on Runloop

> Run OpenCode in secure Runloop devboxes with a fast blueprint workflow.

<Tip>
  Use the reference starter repo for complete source code:
  [runloopai/opencode-starter](https://github.com/runloopai/opencode-starter).
</Tip>

## What you need

* A Runloop API key (`RUNLOOP_API_KEY`)
* One of:
  * Python 3.11+ and [`uv`](https://github.com/astral-sh/uv)
  * Node.js 18+ and `npm`
* Optional model provider keys:
  * `ANTHROPIC_API_KEY`
  * `OPENAI_API_KEY`

## Environment variables

Set your Runloop API key before running any command:

```bash theme={null}
export RUNLOOP_API_KEY="your-runloop-api-key"
```

You can also set provider keys if your OpenCode workflow needs them:

```bash theme={null}
export ANTHROPIC_API_KEY="..."
export OPENAI_API_KEY="..."
```

<Note>
  Instead of exporting provider keys directly, you can store them as Runloop
  account secrets and map them into your devbox at runtime. See   [Account
  Secrets](/docs/devboxes/configuration/account-secrets).
</Note>

<Note title="Starter image">
  The starter image is used when you start a devbox without specifying an image, and as the default base when you build a blueprint without providing Dockerfile content. It includes:

  * **Core tools:** jq, sudo
  * **Extras:** dnsutils, iputils-ping, less, vim, rsync,
    gh
  * **Python stack:** Python 3.12, pip, uv
  * **Node stack:** Node 22.15.0, npm, Yarn 1.22.22 via
    corepack
</Note>

## Setup

Clone the reference implementation:

```bash theme={null}
git clone https://github.com/runloopai/opencode-starter.git
cd opencode-starter
```

## Create a blueprint with OpenCode

<CodeGroup>
  ```bash Python theme={null}
  cd python
  uv sync
  uv run opencode-runloop create-blueprint
  ```

  ```bash TypeScript theme={null}
  cd ts
  npm install
  npm run create-blueprint
  ```
</CodeGroup>

This creates a reusable `opencode` blueprint with OpenCode preinstalled.

## Create a devbox with OpenCode

<CodeGroup>
  ```bash Python theme={null}
  cd python
  uv run opencode-runloop run
  ```

  ```bash TypeScript theme={null}
  cd ts
  npm run run-opencode
  ```
</CodeGroup>

Each command creates a devbox, starts `opencode web`, enables a tunnel, and prints the OpenCode URL.

### Optional: create devbox without blueprint (manual install)

Use this when you want zero upfront setup:

<CodeGroup>
  ```bash Python theme={null}
  cd python
  uv sync
  uv run opencode-runloop run --manual
  ```

  ```bash TypeScript theme={null}
  cd ts
  npm install
  npm run run-opencode -- --manual
  ```
</CodeGroup>

<Note>
  Manual mode is slower because OpenCode is installed in a fresh devbox each
  run.
</Note>

## How the integration works internally

Each run follows this flow:

1. Create a devbox (from blueprint or fresh)
2. Install OpenCode (manual mode only)
3. Write OpenCode config in the devbox
4. Start OpenCode on `0.0.0.0:3000`
5. Create a Runloop tunnel and print the URL

This makes local setup simple while keeping execution remote and sandboxed.

## OpenCode Dockerfile

Use this Dockerfile when creating the blueprint:

```dockerfile theme={null}
# Runloop starter image containing Node.js and npm
FROM runloop:runloop/starter-x86_64

# Install OpenCode globally
RUN npm install -g opencode-ai

# Create config directory
RUN mkdir -p /home/user/.config/opencode

WORKDIR /home/user
```

## OpenCode config

Use this OpenCode config payload:

```json theme={null}
{
  "$schema": "https://opencode.ai/config.json",
  "default_agent": "runloop",
  "server": {
    "hostname": "0.0.0.0",
    "port": 3000
  },
  "agent": {
    "runloop": {
      "description": "Runloop sandbox-aware coding agent",
      "mode": "primary",
      "prompt": "You are running in a Runloop devbox. Use /home/user as your working directory. When running services, bind to 0.0.0.0 so they are accessible via Runloop tunnels. File paths should be absolute or relative to /home/user."
    }
  }
}
```

## Write config into the devbox filesystem

The starter writes config to `/home/user/.config/opencode/opencode.json` before starting OpenCode:

<CodeGroup>
  ```python Python theme={null}
  import json

  opencode_config = {
      "$schema": "https://opencode.ai/config.json",
      "default_agent": "runloop",
      "server": {
          "hostname": "0.0.0.0",
          "port": 3000,
      },
      "agent": {
          "runloop": {
              "description": "Runloop sandbox-aware coding agent",
              "mode": "primary",
              "prompt": "You are running in a Runloop devbox. Use /home/user as your working directory. When running services, bind to 0.0.0.0 so they are accessible via Runloop tunnels. File paths should be absolute or relative to /home/user.",
          },
      },
  }

  devbox.cmd.exec("mkdir -p ~/.config/opencode")
  devbox.file.write(
      file_path="/home/user/.config/opencode/opencode.json",
      contents=json.dumps(opencode_config, indent=2),
  )
  ```

  ```typescript TypeScript theme={null}
  const opencodeConfig = {
    $schema: "https://opencode.ai/config.json",
    default_agent: "runloop",
    server: {
      hostname: "0.0.0.0",
      port: 3000,
    },
    agent: {
      runloop: {
        description: "Runloop sandbox-aware coding agent",
        mode: "primary",
        prompt:
          "You are running in a Runloop devbox. Use /home/user as your working directory. When running services, bind to 0.0.0.0 so they are accessible via Runloop tunnels. File paths should be absolute or relative to /home/user.",
      },
    },
  };

  await devbox.cmd.exec("mkdir -p ~/.config/opencode");
  await devbox.file.write({
    file_path: "/home/user/.config/opencode/opencode.json",
    contents: JSON.stringify(opencodeConfig, null, 2),
  });
  ```
</CodeGroup>

## Snippets

### Creating a blueprint with OpenCode

<CodeGroup>
  ```python Python theme={null}
  from runloop_api_client import RunloopSDK
  from runloop_api_client.types.shared_params.launch_parameters import (
      LaunchParameters,
      UserParameters,
  )

  runloop = RunloopSDK()

  opencode_dockerfile = '''

  # Runloop starter image containing Node.js and npm

  FROM runloop:runloop/starter-x86_64

  # Install OpenCode globally

  RUN npm install -g opencode-ai

  # Create config directory

  RUN mkdir -p /home/user/.config/opencode

  WORKDIR /home/user
  '''

  blueprint = runloop.blueprint.create(
      name="my_opencode_blueprint",
      dockerfile=opencode_dockerfile,
      launch_parameters=LaunchParameters(
          user_parameters=UserParameters(username="root", uid=0)
      ),
  )

  ```

  ```typescript TypeScript theme={null}
  import { RunloopSDK } from "@runloop/api-client";

  const runloop = new RunloopSDK();

  const opencodeDockerfile = `
  # Runloop starter image containing Node.js and npm
  FROM runloop:runloop/starter-x86_64

  # Install OpenCode globally
  RUN npm install -g opencode-ai

  # Create config directory
  RUN mkdir -p /home/user/.config/opencode

  WORKDIR /home/user
  `;

  const blueprint = await runloop.blueprint.create({
    name: "my_opencode_blueprint",
    dockerfile: opencodeDockerfile,
    launch_parameters: {
      user_parameters: {
        username: "root",
        uid: 0,
      },
    },
  });
  ```
</CodeGroup>

### Creating a devbox with OpenCode

<CodeGroup>
  ```python Python theme={null}
  from runloop_api_client import RunloopSDK
  from runloop_api_client.types.devbox_create_params import Tunnel
  from runloop_api_client.types.shared_params.launch_parameters import (
      LaunchParameters,
      UserParameters,
  )

  runloop = RunloopSDK()

  devbox = runloop.devbox.create_from_blueprint_name(
      name="opencode",
      blueprint_name="my_opencode_blueprint",
      launch_parameters=LaunchParameters(
          user_parameters=UserParameters(username="user", uid=1000),
      ),
      tunnel=Tunnel(auth_mode="open"),
  )

  ```

  ```typescript TypeScript theme={null}
  import { RunloopSDK } from "@runloop/api-client";

  const runloop = new RunloopSDK();

  const devbox = await runloop.devbox.createFromBlueprintName("my_opencode_blueprint", {
    name: "opencode",
    launch_parameters: {
      user_parameters: {
        username: "user",
        uid: 1000,
      },
    },
    tunnel: {
      auth_mode: "open",
    },
  });
  ```
</CodeGroup>

## Common issues

* **Missing API key errors**
  * Confirm `RUNLOOP_API_KEY` is set in your shell before running commands.
* **Provider/model errors**
  * Set `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` based on the model you are using.
* **Slow startup**
  * Use blueprint mode (`create-blueprint` once, then `run`) instead of `--manual`.

## Next steps

* Start from the source reference repo: [runloopai/opencode-starter](https://github.com/runloopai/opencode-starter)
* Review [Devbox overview](/docs/devboxes/overview)
* Learn about [Blueprints](/docs/devboxes/blueprints/overview)
* Learn how [Tunnels](/docs/devboxes/tunnels) expose local services securely
