# Deploy OpenClaw (/docs/agents/openclaw/openclaw-gateway)

<!-- agent-signals: reading_time_min: 7 · est_tokens: 3444 · updated: 2026-07-30 -->
Related: [OpenClaw Telegram](/docs/agents/openclaw/openclaw-telegram.md), [OpenAI Agents SDK](/docs/agents/openai-agents-sdk.md)

## Quick start [#quick-start]

This launches your OpenClaw [gateway](https://docs.openclaw.ai/gateway) site (web UI for chatting with agents).

<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 TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token'
      const PORT = 18789

      // 1. Create sandbox
      const sandbox = await Sandbox.create('openclaw', {
        envs: { OPENAI_API_KEY: process.env.OPENAI_API_KEY },
        timeoutMs: 3600_000,
      })

      // 2. Set the default model
      await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2')

      // 3. Set insecure control UI flags and start the gateway with token auth
      await sandbox.commands.run(
        `bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && ` +
          `openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && ` +
          `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}'`,
        { background: true }
      )

      // 4. Wait for the gateway to start listening
      for (let i = 0; i < 45; i++) {
        const probe = await sandbox.commands.run(
          `bash -lc 'ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting'`
        )
        if (probe.stdout.trim() === 'ready') break
        await new Promise((r) => setTimeout(r, 1000))
      }

      const url = `https://${sandbox.getHost(PORT)}/?token=${TOKEN}`
      console.log(`Gateway: ${url}`)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      import os, time
      from e2b import Sandbox

      TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token")
      PORT = 18789

      # 1. Create sandbox
      sandbox = Sandbox.create("openclaw", envs={
          "OPENAI_API_KEY": os.environ["OPENAI_API_KEY"],
      }, timeout=3600)

      # 2. Set the default model
      sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2")

      # 3. Set insecure control UI flags and start the gateway with token auth
      sandbox.commands.run(
          f"bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && "
          f"openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && "
          f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}'",
          background=True,
      )

      # 4. Wait for the gateway to start listening
      for _ in range(45):
          probe = sandbox.commands.run(
              f'bash -lc \'ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting\''
          )
          if probe.stdout.strip() == "ready":
              break
          time.sleep(1)

      url = f"https://{sandbox.get_host(PORT)}/?token={TOKEN}"
      print(f"Gateway: {url}")
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

Visit the printed `Gateway` URL in your browser.

If you run in secure mode (set `gateway.controlUi.dangerouslyDisableDeviceAuth false`), run this after opening the URL to poll pending pairing requests and approve the first one.

<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  
      // 5. Poll for the browser's pending device request and approve it
      for (let i = 0; i < 30; i++) {
        try {
          const res = await sandbox.commands.run(
            `openclaw devices list --json --url ws://127.0.0.1:${PORT} --token ${TOKEN}`
          )
          const data = JSON.parse(res.stdout)
          if (data.pending?.length) {
            const rid = data.pending[0].requestId
            await sandbox.commands.run(
              `openclaw devices approve ${rid} --token ${TOKEN} --url ws://127.0.0.1:${PORT}`
            )
            console.log(`Device approved: ${rid}`)
            break
          }
        } catch {}
        await new Promise((r) => setTimeout(r, 2000))
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      import json

      # 5. Poll for the browser's pending device request and approve it
      for _ in range(30):
          try:
              res = sandbox.commands.run(
                  f"openclaw devices list --json --url ws://127.0.0.1:{PORT} --token {TOKEN}"
              )
              data = json.loads(res.stdout)
              if data.get("pending"):
                  rid = data["pending"][0]["requestId"]
                  sandbox.commands.run(
                      f"openclaw devices approve {rid} --token {TOKEN} --url ws://127.0.0.1:{PORT}"
                  )
                  print(f"Device approved: {rid}")
                  break
          except Exception:
              pass
          time.sleep(2)
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

Once approved, the browser connects and the gateway UI loads.

## How it works [#how-it-works]

| Step                         | What happens                                                                 |
| ---------------------------- | ---------------------------------------------------------------------------- |
| `--bind lan`                 | Gateway listens on `0.0.0.0` so E2B can proxy it                             |
| `--auth token`               | Requires `?token=` on the URL for HTTP and WebSocket auth                    |
| Browser opens URL            | Gateway serves the UI, browser opens a WebSocket                             |
| `code=1008 pairing required` | Gateway closes the WebSocket until the device is approved (secure mode only) |
| `devices approve`            | Approves the browser's device fingerprint (secure mode only)                 |
| Browser reconnects           | WebSocket connects successfully, UI is live                                  |

## Gateway flags reference [#gateway-flags-reference]

| Flag                   | Purpose                                            |
| ---------------------- | -------------------------------------------------- |
| `--allow-unconfigured` | Start without a full config file                   |
| `--bind lan`           | Bind to `0.0.0.0` (required for E2B port proxying) |
| `--auth token`         | Enable token-based authentication                  |
| `--token <value>`      | The auth token (passed as `?token=` in the URL)    |
| `--port <number>`      | Gateway listen port (default: `18789`)             |

## How to restart the gateway [#how-to-restart-the-gateway]

Use this when the gateway is already running and you want a clean restart (for example, after changing model or env settings).

<Info>
  We can't use the `openclaw gateway restart` command here. Some SDK environments cannot target a specific Unix user in `commands.run`. The commands below use the default command user context.
</Info>

<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  
      const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token'
      const PORT = 18789

      // 1) Kill existing gateway processes if present
      await sandbox.commands.run(
        `bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do for pid in $(pgrep -f "$p" || true); do kill "$pid" >/dev/null 2>&1 || true; done; done'`
      )
      await new Promise((r) => setTimeout(r, 1000))

      // 2) Start gateway again
      await sandbox.commands.run(
        `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}`,
        { background: true }
      )

      // 3) Wait for listening socket
      for (let i = 0; i < 45; i++) {
        const probe = await sandbox.commands.run(
          `bash -lc 'ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting'`
        )
        if (probe.stdout.trim() === 'ready') break
        await new Promise((r) => setTimeout(r, 1000))
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      import os, time

      TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token")
      PORT = 18789

      # 1) Kill existing gateway processes if present
      sandbox.commands.run(
          """bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do
      for pid in $(pgrep -f "$p" || true); do
        kill "$pid" >/dev/null 2>&1 || true
      done
      done'"""
      )
      time.sleep(1)

      # 2) Start gateway again
      sandbox.commands.run(
          f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}",
          background=True,
      )

      # 3) Wait for listening socket
      for _ in range(45):
          probe = sandbox.commands.run(
              f'bash -lc \'ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting\''
          )
          if probe.stdout.strip() == "ready":
              break
          time.sleep(1)
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Turn insecure flags off (recommended after testing) [#turn-insecure-flags-off-recommended-after-testing]

Use this to restore secure device authentication after initial testing.

<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  
      const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token'
      const PORT = 18789

      await sandbox.commands.run(
        `bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth false && ` +
          `openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth false'`
      )

      await sandbox.commands.run(
        `bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do for pid in $(pgrep -f "$p" || true); do kill "$pid" >/dev/null 2>&1 || true; done; done'`
      )

      await sandbox.commands.run(
        `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}`,
        { background: true }
      )
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      import os

      TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token")
      PORT = 18789

      sandbox.commands.run(
          "bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth false && "
          "openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth false'"
      )

      sandbox.commands.run(
          """bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do
      for pid in $(pgrep -f "$p" || true); do
        kill "$pid" >/dev/null 2>&1 || true
      done
      done'"""
      )

      sandbox.commands.run(
          f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}",
          background=True,
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Related [#related]

<CardGroup cols="1">
  <Card title="OpenClaw Telegram" 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;M21.5 12C21.5 17.2467 17.2467 21.5 12 21.5C10.3719 21.5 8.8394 21.0904 7.5 20.3687C5.63177 19.362 4.37462 20.2979 3.26592 20.4658C3.09774 20.4913 2.93024 20.4302 2.80997 20.31C2.62741 20.1274 2.59266 19.8451 2.6935 19.6074C3.12865 18.5818 3.5282 16.6382 2.98341 15C2.6698 14.057 2.5 13.0483 2.5 12C2.5 6.75329 6.75329 2.5 12 2.5C17.2467 2.5 21.5 6.75329 21.5 12Z&#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.1257 12H12.0007M8.125 12H8M16.125 12H16M12.2507 12C12.2507 12.1381 12.1388 12.25 12.0007 12.25C11.8627 12.25 11.7507 12.1381 11.7507 12C11.7507 11.8619 11.8627 11.75 12.0007 11.75C12.1388 11.75 12.2507 11.8619 12.2507 12ZM8.25 12C8.25 12.1381 8.13807 12.25 8 12.25C7.86193 12.25 7.75 12.1381 7.75 12C7.75 11.8619 7.86193 11.75 8 11.75C8.13807 11.75 8.25 11.8619 8.25 12ZM16.25 12C16.25 12.1381 16.1381 12.25 16 12.25C15.8619 12.25 15.75 12.1381 15.75 12C15.75 11.8619 15.8619 11.75 16 11.75C16.1381 11.75 16.25 11.8619 16.25 12Z&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/docs/agents/openclaw/openclaw-telegram">
    Connect OpenClaw to Telegram and approve pairing
  </Card>
</CardGroup>
