#What's the correct way to set up a containerized second agent under an existing gateway?
1 messages · Page 1 of 1 (latest)
Yes — the right pattern is:
Keep Gateway on bare metal, and run only the second agent in a Docker sandbox.
On macOS that works fine (Docker Desktop required).
Minimal setup
- Install/start Docker Desktop on the Mac.
- Build the sandbox image (from an OpenClaw source checkout):
scripts/sandbox-setup.sh
- Add a second agent in
~/.openclaw/openclaw.jsonwith sandbox enabled:
{
agents: {
list: [
{
id: "main",
default: true,
workspace: "~/.openclaw/workspace",
sandbox: { mode: "off" }
},
{
id: "worker",
workspace: "~/.openclaw/workspace-worker",
sandbox: {
mode: "all",
scope: "agent" // one container for this agent
}
}
]
},
// route selected traffic to the containerized agent
bindings: [
{ agentId: "worker", match: { channel: "discord", accountId: "*" } }
]
}
- Restart gateway:
openclaw gateway restart
- Verify:
openclaw agents list --bindings
docker ps --filter "name=openclaw-sbx-"
Important gotcha
Auth is per-agent. Your new worker agent has its own auth store (auth-profiles.json), so configure/copy creds for it or it may fail with missing provider credentials.
Docs:
If you want, I can give you a Discord-thread-specific binding snippet so only this thread uses the containerized agent while everything else stays on main.
Perfect — then you want one-agent-per-human, with that user’s agent sandboxed.
That gives you both:
- hard context separation (workspace + sessions + auth), and
- containerized execution for that agent.
What to do
- Create a dedicated agent for that human (
openclaw agents add <id>). - Give it its own workspace (and optionally explicit
agentDir). - Set that agent to
sandbox.mode: "all"+scope: "agent". - Route that human’s traffic to that agent via
bindings.
Why this works
Each agent already has isolated state:
- workspace files (AGENTS/SOUL/MEMORY etc.)
- session store (
~/.openclaw/agents/<agentId>/sessions) - auth profiles (
.../agent/auth-profiles.json)
So context won’t leak across agents unless you intentionally bridge it.
Config shape (example)
{
agents: {
list: [
{ id: "main", default: true, workspace: "~/.openclaw/workspace", sandbox: { mode: "off" } },
{
id: "user2",
workspace: "~/.openclaw/workspace-user2",
sandbox: { mode: "all", scope: "agent" }
}
]
},
bindings: [
// bind this user's traffic to user2 (account/peer/channel match as needed)
{ agentId: "user2", match: { channel: "discord", accountId: "*" } }
]
}
Then openclaw gateway restart and verify with:
openclaw agents list --bindings
docker ps --filter "name=openclaw-sbx-"
If you want, I can help you pick the exact Discord binding strategy (per-account vs per-user/peer vs per-role) for your topology.
Docs: https://docs.openclaw.ai/concepts/multi-agent · https://docs.openclaw.ai/tools/multi-agent-sandbox-tools · https://docs.openclaw.ai/gateway/sandboxing
Yes — you can do that, but that becomes a separate OpenClaw instance/gateway, not a second agent under your current gateway.
- Same gateway + second agent (recommended for your case): easier ops, still isolated context per agent.
- New macOS user + fresh setup: strongest isolation (OS-level), but more overhead.
If you go the new-user route, treat it as “run another gateway”:
- Separate state/workspace/config (new user gives this naturally).
- Different port (avoid
18789collision). - Separate channel credentials/accounts where needed.
- Separate daemon/service under that user.
So: valid approach, just heavier.
If you only need separate human context + containerized execution, multi-agent + per-agent sandbox is the cleaner path.
Docs: https://docs.openclaw.ai/concepts/multi-agent · https://docs.openclaw.ai/gateway/sandboxing · https://docs.openclaw.ai/gateway/multiple-gateways
Great question. In one gateway + multiple agents, isolation is mixed:
openclaw.json→ shared (one gateway-wide config file).- Cron job store/logs (
~/.openclaw/cron/jobs.json,~/.openclaw/cron/runs/...) → shared. - But each job can be pinned to an
agentId, so execution context can still be agent-specific. - Auth profiles (
~/.openclaw/agents/<agentId>/agent/auth-profiles.json) → isolated per agent. - Sessions/transcripts (
~/.openclaw/agents/<agentId>/sessions/...) → isolated per agent. - Workspace/memory files (per-agent workspace path) → isolated per agent.
So: context/auth/session isolation is solid per agent, but scheduler/config files are gateway-global.
If you need everything isolated (including config + cron store), that’s when you run separate gateways (profile or OS-user split).
Refs: https://docs.openclaw.ai/concepts/multi-agent · https://docs.openclaw.ai/automation/cron-jobs · https://docs.openclaw.ai/help/faq#where-does-openclaw-store-its-data
Yep — this is most likely one of two things on the second macOS user:
- global npm prefix not writable (common with separate user + Homebrew Node), and/or
- Node 25 edge-case (OpenClaw recommends Node 24).
Quick triage first (please paste output):
openclaw --version || true
node -v
npm -v
npm prefix -g
ls -ld "$(npm prefix -g)" "$(npm prefix -g)/lib" "$(npm prefix -g)/bin" 2>/dev/null
Fast fix path I’d use
A) Put npm globals in user-writable dir
mkdir -p "$HOME/.npm-global"
npm config set prefix "$HOME/.npm-global"
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.zshrc
exec zsh -l
B) Use Node 24 (recommended)
brew install node@24
echo 'export PATH="/opt/homebrew/opt/node@24/bin:$PATH"' >> ~/.zprofile
exec zsh -l
node -v
C) Install with pnpm (preferred)
corepack enable
pnpm add -g openclaw@latest
pnpm approve-builds -g
openclaw --version
If you want to keep using the installer, rerun with verbose and paste the tail:
curl -fsSL https://openclaw.ai/install.sh | bash -s -- --verbose --no-onboard
Docs: https://docs.openclaw.ai/install/node · https://docs.openclaw.ai/install · https://docs.openclaw.ai/help/faq#installer-stuck-how-do-i-get-more-feedback
You’re not wrong — macOS multi-OS-user + Homebrew is exactly where this gets annoying.
The easiest fix: don’t split by macOS user.
Run both gateways under one macOS account using OpenClaw profiles (this is the intended multi-gateway path).
Why profiles are better here
Each profile gets isolated:
- config
- state dir
- sessions
- auth
- cron store
- workspace
So you still get strong separation, without Homebrew permission hell.
Suggested layout
# existing gateway (main)
openclaw --profile main status
# second isolated gateway
openclaw --profile user2 onboard
# pick a DIFFERENT base port in onboarding (e.g. 19789; keep ~20 port gap)
openclaw --profile user2 gateway install
openclaw --profile user2 gateway start
# sanity checks
openclaw --profile main status
openclaw --profile user2 status
If you insist on separate macOS users, I’d avoid Homebrew there and use a user-local Node toolchain (fnm/nvm + user-local npm/pnpm prefix), but profiles are much cleaner.
You’re absolutely right about one gateway + multi-agent: some things are shared (notably gateway-level config/scheduler).
But if you run multiple gateways (with profiles or separate container/host), you do not share those files.
For profile-based multi-gateway, each profile gets its own state tree (default):
~/.openclaw-main/...~/.openclaw-user2/...
That includes separate:
openclaw.jsoncron/jobs.json+ cron logsagents/<agentId>/agent/auth-profiles.json- sessions/transcripts
- channel creds
Shared is basically just:
- installed binary/runtime (Node/OpenClaw install)
- OS-level deps (e.g., Docker Desktop)
- anything you intentionally point to the same path
So if your requirement is “no shared operational files,” the answer is:
don’t do multi-agent in one gateway; do two gateways (profiles or separate container/VM).
Refs: https://docs.openclaw.ai/gateway/multiple-gateways · https://docs.openclaw.ai/install/docker
Yes — absolutely.
If you want hard isolation, running the second gateway in its own container is the cleanest route.
For true isolation, make sure the second gateway has:
- its own state dir (
/home/node/.openclawin that container) - its own workspace
- its own port (e.g. host
19789 -> container 18789) - its own auth/channel creds (or at least non-overlapping routing)
And importantly: do not mount the first gateway’s ~/.openclaw or workspace into that second container.
At that point, file/state isolation is real; the only shared layer is the host OS + Docker runtime.
Docs:
If you want, I can sketch a minimal two-gateway docker/host topology (host gateway + one container gateway) with port/state layout.
Yep — here’s the clean way to run a second gateway fully in Docker while your first one stays bare metal.
1) Pick isolated paths + port (do not reuse first gateway paths)
Use a different state/workspace and port (example 19789):
export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:latest"
export OPENCLAW_GATEWAY_PORT="19789"
export OPENCLAW_GATEWAY_BIND="lan"
export OPENCLAW_CONFIG_DIR="$HOME/.openclaw-gw2"
export OPENCLAW_WORKSPACE_DIR="$HOME/.openclaw-gw2/workspace"
mkdir -p "$OPENCLAW_WORKSPACE_DIR"
2) Run Docker setup in its own repo folder
git clone https://github.com/openclaw/openclaw.git ~/openclaw-gw2
cd ~/openclaw-gw2
./scripts/docker/setup.sh
That script handles onboarding + compose startup for the containerized gateway.
3) Verify second gateway is up
docker compose ps
curl -fsS "http://127.0.0.1:${OPENCLAW_GATEWAY_PORT}/healthz"
Open Control UI for gateway #2:
http://127.0.0.1:19789/
Agent isolation model
- This second gateway already has its own independent default agent (
main) in its own state/workspace. - If you want a named agent inside gateway #2, add it there:
docker compose run --rm openclaw-cli agents add user2
Important guardrails
- Keep gateway #1 on
18789, gateway #2 on19789(or another non-overlapping base port). - Don’t point both gateways at the same
OPENCLAW_CONFIG_DIR/ workspace. - Prefer separate channel accounts/tokens per gateway to avoid duplicate replies.
Docs:
If you want, I can give you a ready-to-paste .env + docker-compose.yml for the exact “bare metal gateway #1 + container gateway #2” topology.
Yeah, that’s a legit complaint — and you’re right to call it out.
What usually causes that “it’s using host install” feeling is either:
- onboarding command ran on host by accident, or
- container got mounted to host
~/.openclawinstead of a fresh path.
Let’s do a hard-isolated flow (no setup.sh, no ambiguity):
mkdir -p ~/openclaw-gw2
cd ~/openclaw-gw2
mkdir -p "$HOME/.openclaw-gw2/workspace"
Create docker-compose.yml:
services:
gw2:
image: ghcr.io/openclaw/openclaw:latest
container_name: openclaw-gw2
restart: unless-stopped
environment:
HOME: /home/node
NODE_ENV: production
volumes:
- ${HOME}/.openclaw-gw2:/home/node/.openclaw
- ${HOME}/.openclaw-gw2/workspace:/home/node/.openclaw/workspace
ports:
- "127.0.0.1:19789:18789"
command:
["node","dist/index.js","gateway","--bind","lan","--port","18789","--allow-unconfigured"]
Now run onboarding inside container only:
docker compose run --rm gw2 node dist/index.js onboard
Then start gateway #2:
docker compose up -d gw2
Check it:
docker compose ps
curl -fsS http://127.0.0.1:19789/healthz
This guarantees gateway #2 uses only ~/.openclaw-gw2/*, not your host gateway state.
If you want, paste your current compose/env and I’ll sanity-check it line-by-line.
Perfect — let’s do a clean multi-agent + sandbox setup on one gateway.
1) Preflight
openclaw --version
openclaw status
docker version
2) Build sandbox image (once)
From an OpenClaw source checkout:
git clone https://github.com/openclaw/openclaw.git ~/openclaw-ref
cd ~/openclaw-ref
scripts/sandbox-setup.sh
3) Create second agent
openclaw agents add worker --workspace ~/.openclaw/workspace-worker
4) Enable sandbox only for that agent
Edit ~/.openclaw/openclaw.json and set/merge this into the worker agent entry:
{
agents: {
list: [
{
id: "worker",
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "none"
},
tools: {
allow: ["read"],
deny: ["exec", "write", "edit", "apply_patch", "process", "browser"]
}
}
]
}
}
(Leave your main agent unsandboxed with sandbox.mode: "off" if you want.)
5) Route traffic to worker
If you have a separate Discord account for worker:
openclaw agents bind --agent worker --bind discord:worker
(If same account + specific thread/channel routing, we can do a bindings entry for that peer.)
6) Restart + verify
openclaw gateway restart
openclaw agents list --bindings
openclaw sandbox explain --agent worker
docker ps --filter "name=openclaw-sbx-"
Important gotcha
Auth is per-agent. worker has its own auth store:
~/.openclaw/agents/worker/agent/auth-profiles.json
So configure creds for worker (or copy intentionally).
Docs:
- https://docs.openclaw.ai/tools/multi-agent-sandbox-tools
- https://docs.openclaw.ai/gateway/sandboxing
- https://docs.openclaw.ai/concepts/multi-agent
If you paste your current agents.list + bindings, I’ll give you the exact minimal diff.
Short answer: not fully, yet.
There’s no dedicated “create sandboxed agent” wizard end-to-end right now.
The current flow is:
- Interactive-ish agent creation
openclaw agents add worker
-
Manual per-agent sandbox toggle in
openclaw.json(agents.list[].sandbox). -
Validate/recreate runtime
openclaw sandbox explain --agent worker
openclaw sandbox recreate --agent worker
So you get interactive setup for the agent itself, but sandboxing is still config-driven.
If you want, paste your current worker agent block and I’ll give you an exact minimal patch (copy/paste-safe).
Docs: https://docs.openclaw.ai/tools/multi-agent-sandbox-tools · https://docs.openclaw.ai/cli/sandbox
Perfect — for that exact assistant agent, use this config shape:
{
id: "assistant",
name: "assistant",
workspace: "/Users/myuser/.openclaw/workspace-assistant",
agentDir: "/Users/myuser/.openclaw/agents/assistant/agent",
sandbox: {
mode: "all", // always sandbox this agent
scope: "agent", // one persistent sandbox per agent
backend: "docker",
workspaceAccess: "rw", // sandbox can read/write workspace; changes persist
browser: {
enabled: true, // use sandboxed browser container
autoStart: true
}
// docker.network defaults to "none"; set to "bridge" only if exec needs internet
}
}
Then do:
- Build sandbox images (once):
cd ~/openclaw-ref
scripts/sandbox-setup.sh
scripts/sandbox-browser-setup.sh
- Apply runtime refresh:
openclaw sandbox recreate --agent assistant
openclaw gateway restart
- Verify:
openclaw sandbox explain --agent assistant
docker ps --filter "name=openclaw-sbx-"
If you currently have restrictive tool allow/deny policies, make sure assistant can use:
exec, process, browser, read, write, edit, apply_patch.
If you paste your full agents.list + tools block, I’ll give you an exact no-surprises merged config.