# OpenCode (/docs/agents/opencode)

<!-- agent-signals: reading_time_min: 5 · est_tokens: 3262 · updated: 2026-07-30 -->
Related: [Amp](/docs/agents/amp.md), [Claude Code](/docs/agents/claude-code.md), [Claude Managed Agents](/docs/agents/claude-managed-agents.md), [Codex](/docs/agents/codex.md), [Crabbox with E2B](/docs/agents/crabbox.md), [Devin](/docs/agents/devin.md)

[OpenCode](https://opencode.ai) is an open-source coding agent that supports multiple LLM providers. E2B provides a pre-built `opencode` template with OpenCode already installed.

## CLI [#cli]

Create a sandbox with the [E2B CLI](/docs/cli).

```bash
e2b sbx create opencode
```

Once inside the sandbox, start OpenCode.

```bash
opencode
```

## Run headless [#run-headless]

Use `opencode run` for non-interactive mode. Pass your LLM provider's API key as an environment variable — OpenCode supports `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, and [others](https://opencode.ai/docs/config/).

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      import { Sandbox } from 'e2b'

      const sandbox = await Sandbox.create('opencode', {
        envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
      })

      const result = await sandbox.commands.run(
        `opencode run "Create a hello world HTTP server in Go"`
      )

      console.log(result.stdout)
      await sandbox.kill()
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      import os
      from e2b import Sandbox

      sandbox = Sandbox.create("opencode", envs={
          "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
      })

      result = sandbox.commands.run(
          'opencode run "Create a hello world HTTP server in Go"',
      )

      print(result.stdout)
      sandbox.kill()
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

### Example: work on a cloned repository [#example-work-on-a-cloned-repository]

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      import { Sandbox } from 'e2b'

      const sandbox = await Sandbox.create('opencode', {
        envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
        timeoutMs: 600_000,
      })

      await sandbox.git.clone('https://github.com/your-org/your-repo.git', {
        path: '/home/user/repo',
        username: 'x-access-token',
        password: process.env.GITHUB_TOKEN,
        depth: 1,
      })

      const result = await sandbox.commands.run(
        `cd /home/user/repo && opencode run "Add error handling to all API endpoints"`,
        { onStdout: (data) => process.stdout.write(data) }
      )

      const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
      console.log(diff.stdout)

      await sandbox.kill()
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      import os
      from e2b import Sandbox

      sandbox = Sandbox.create("opencode", envs={
          "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
      }, timeout=600)

      sandbox.git.clone("https://github.com/your-org/your-repo.git",
          path="/home/user/repo",
          username="x-access-token",
          password=os.environ["GITHUB_TOKEN"],
          depth=1,
      )

      result = sandbox.commands.run(
          'cd /home/user/repo && opencode run "Add error handling to all API endpoints"',
          on_stdout=lambda data: print(data, end=""),
      )

      diff = sandbox.commands.run("cd /home/user/repo && git diff")
      print(diff.stdout)

      sandbox.kill()
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Connect with the OpenCode SDK [#connect-with-the-opencode-sdk]

OpenCode includes a [headless HTTP server](https://opencode.ai/docs/server/) that you can control programmatically using the [`@opencode-ai/sdk`](https://opencode.ai/docs/sdk/) client. Start the server inside a sandbox, get the public URL with `sandbox.getHost()`, and connect from your application.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      import { Sandbox } from 'e2b'
      import { createOpencodeClient } from '@opencode-ai/sdk'

      const sandbox = await Sandbox.create('opencode', {
        envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
        lifecycle: {
          onTimeout: 'pause', // "pause" | "kill"
        },
        timeoutMs: 10 * 60 * 1000,
      })

      // Start the OpenCode server
      sandbox.commands.run('opencode serve --hostname 0.0.0.0 --port 4096', {
        background: true,
      })

      // Wait for the server to be ready
      const host = sandbox.getHost(4096)
      const baseUrl = `https://${host}`
      while (true) {
        try {
          await fetch(`${baseUrl}/global/health`)
          break
        } catch {
          await new Promise((r) => setTimeout(r, 500))
        }
      }

      // Connect to the server
      const client = createOpencodeClient({
        baseUrl,
      })

      // Create a session and send a prompt
      const { data: session } = await client.session.create({
        body: { title: 'E2B Session' },
      })
      const { data: result } = await client.session.prompt({
        path: { id: session.id },
        body: {
          parts: [{ type: 'text', text: 'Create a hello world HTTP server in Go' }],
        },
      })
      console.log(result)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      import os
      import time
      import requests
      from e2b import Sandbox

      sandbox = Sandbox.beta_create("opencode", envs={
          "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
      }, auto_pause=True, timeout=10 * 60)

      # Start the OpenCode server
      sandbox.commands.run(
          "opencode serve --hostname 0.0.0.0 --port 4096",
          background=True,
      )

      # Wait for the server to be ready
      host = sandbox.get_host(4096)
      base_url = f"https://{host}"
      while True:
          try:
              requests.get(f"{base_url}/global/health")
              break
          except requests.ConnectionError:
              time.sleep(0.5)

      # Create a session and send a prompt via the HTTP API
      session = requests.post(f"{base_url}/session").json()
      result = requests.post(
          f"{base_url}/session/{session['id']}/message",
          json={
              "parts": [{"type": "text", "text": "Create a hello world HTTP server in Go"}],
          },
      ).json()
      print(result)
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Build a custom template [#build-a-custom-template]

If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-built `opencode` template.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      // template.ts
      import { Template, waitForPort } from 'e2b'

      export const template = Template()
        .fromTemplate('opencode')
        .setEnvs({
          OPENCODE_SERVER_PASSWORD: 'your-password',
        })
        // Optional - start the OpenCode server on sandbox start
        .setStartCmd(
          'opencode serve --hostname 0.0.0.0 --port 4096',
          waitForPort(4096)
        )
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      # template.py
      from e2b import Template, wait_for_port

      template = (
          Template()
          .from_template("opencode")
          .set_envs({
              "OPENCODE_SERVER_PASSWORD": "your-password",
          })
          # Optional - start the OpenCode server on sandbox start
          .set_start_cmd(
              "opencode serve --hostname 0.0.0.0 --port 4096",
              wait_for_port(4096)
          )
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      // build.ts
      import { Template, defaultBuildLogger } from 'e2b'
      import { template as openCodeTemplate } from './template'

      await Template.build(openCodeTemplate, 'my-opencode', {
        cpuCount: 2,
        memoryMB: 2048,
        onBuildLogs: defaultBuildLogger(),
      })
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      # build.py
      from e2b import Template, default_build_logger
      from template import template as opencode_template

      Template.build(opencode_template, "my-opencode",
          cpu_count=2,
          memory_mb=2048,
          on_build_logs=default_build_logger(),
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

Run the build script to create the template.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```bash  
      npx tsx build.ts
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```bash  
      python build.py
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Related guides [#related-guides]

<CardGroup cols="3">
  <Card title="Sandbox persistence" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><circle cx=&#x22;12&#x22; cy=&#x22;12&#x22; r=&#x22;10&#x22; stroke=&#x22;currentColor&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M12 8V12L14 14&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/docs/sandbox/persistence">
    Auto-pause, resume, and manage sandbox lifecycle
  </Card>

  <Card title="Git integration" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M16 6.99998L19.0664 9.64296C20.3554 10.7541 21 11.3096 21 12C21 12.6903 20.3555 13.2459 19.0664 14.357L16 17&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M8 6.99998L4.93365 9.64296C3.64455 10.7541 3 11.3096 3 12C3 12.6903 3.64455 13.2459 4.93365 14.357L8 17&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/docs/sandbox/git-integration">
    Clone repos, manage branches, and push changes
  </Card>

  <Card title="SSH access" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M4.00004 17C4.00004 17 9.99999 12.5811 10 11C10 9.41884 4 5 4 5&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M12 19H20&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/docs/sandbox/ssh-access">
    Connect to the sandbox via SSH for interactive sessions
  </Card>
</CardGroup>
