The shortest answer to “How to run OpenClaw safely with minimal permissions” is: start with less than you think it needs, watch it break, then give it the smallest specific capability that unblocks the job. Everything below is the long answer — the playbook I ended up with after putting OpenClaw 0.27.3 behind a throttled, read-only sandbox and slowly opening the gates.
Why bother with least privilege?
OpenClaw can launch a browser, hit your shell, talk to eight hundred SaaS APIs, and schedule cron-like background tasks. That’s powerful and, by definition, dangerous. The upstream docs mention tightening permissions but stop at “consider it.” If you run agents on a workstation that also holds SSH keys or production kube configs, considering is not enough.
Threat model snapshot:
- Prompt injection tricking the agent into exfiltration
- Dependency compromise (npm install is forever a gamble)
- Misconfiguration leaking environment variables
- Accidental self-DDOS by infinite loops or aggressive crawling
Principle of least privilege means the agent only has what it needs now, not what it might need later. We’ll implement that in layers: filesystem, network, process, and API credentials.
What OpenClaw assumes out of the box
Fresh install steps:
nvm install 22
npm install -g openclaw@0.27.3
openclaw init my-agent
openclaw gateway
That launches a local gateway listening on 127.0.0.1:3000 and spawns the daemon. By default it:
- Reads and writes inside the project directory
- Has network egress to anywhere, any verb (GET/POST/PATCH…)
- Executes
/bin/bashwhen a task invokes the shell tool - Persists memory under
.openclaw/storage/
There is no built-in RBAC layer — you are expected to sandbox at the OS/container level. Luckily, node processes are easy to fence off with namespaces and seccomp.
Step 1 – Read-only filesystem + GET-only web
Run the agent in a tmpfs jail
Create a minimal runtime directory and mount it read-only. On Linux:
mkdir -p /srv/openclaw-ro
chmod 755 /srv/openclaw-ro
mount ‑o bind,ro /srv/openclaw-ro /srv/openclaw-ro
Copy only what the agent needs: compiled JS, prompt templates, config JSON. Skip node_modules; we’ll install those inside the container next.
Containerize with Docker + no-new-privileges
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev --ignore-scripts
COPY . .
USER node
CMD ["npx", "openclaw", "gateway"]
Run it locked down:
docker run \
--name claw-ro \
--read-only \
--tmpfs /tmp:size=64m \
--cap-drop ALL \
--security-opt no-new-privileges \
--pids-limit 256 \
--memory 512m \
-p 3000:3000 \
claw:ro
The agent now can’t mutate the on-disk project, can’t gain extra Linux caps, and is boxed into 512 MB RAM. Most importantly, there is no shell in the image, so attempts to call the shell tool fail noisily.
Restrict HTTP verbs with egress firewall
Iptables one-liner:
# allow TCP 80/443 GET/HEAD only
iptables -A OUTPUT -p tcp -m owner --uid-owner $(id -u node) -m multiport --dports 80,443 -m string --string "POST /" --algo bm -j REJECT
Yes, string-matching packets is ugly. For corporate networks, use an egress proxy like Squid and whitelist “GET” and “HEAD”. The key is preventing data exfil via POST/PUT/PATCH while still allowing web search.
At this stage OpenClaw can:
- Read local files
- Call web search tools
- Write to /tmp inside the container only
That is enough for a “research assistant” agent where humans copy results out manually.
Step 2 – Selective write access
I quickly realized read-only breaks two core features: persistent memory and tool caching. The fix is a writable /data volume isolated from everything else:
docker run ... \
--read-only \
--volume claw-state:/data \
--env OPENCLAW_STORAGE_DIR=/data \
claw:ro
Now the agent can save vector embeddings and log conversation history without touching the source tree. Backup claw-state nightly and you’re set.
Step 3 – Grant vetted shell commands only
Sooner or later you’ll want the agent to run npm test or tail a log. Giving back full bash is overkill. I used sudo with a command whitelist:
# /etc/sudoers.d/openclaw
openclaw ALL=(root) NOPASSWD: /usr/local/bin/npm test, /usr/bin/journalctl *
Inside the container, set:
ENV SHELL_WRAPPER="sudo -u root"
The agent can now run exactly two commands. Any other attempt hits “permission denied.”
Alternative: write a tiny Node tool that exposes only the needed operations and register it as a custom OpenClaw tool instead of handing back shell access.
Permission escalation roadmap
Here’s the incremental sequence that worked for me. Each step should come with monitoring (next section) and at least a week of runtime before moving on.
- Research-only mode
Read-only FS, GET-only network. - Stateful mode
Add a writable/datavolume for memory. - Approved shell snippets
Whitelistedsudocommands or custom tool. - GET + specific POST targets
Allow POST only to domains you own (e.g., internal webhook). - SaaS API keys with scoped tokens
Give GitHub PAT withpublic_repoonly, Gmail restricted to a label, etc. - Outbound email / messaging
Enable Composio integrations that can reach users. - Full local shell
Only if the agent needs to compile or deploy. - Cluster admin
At this point you trust it like another SRE — very few teams go this far.
I never progressed past step 6 on production boxes. Step 8 is reserved for staging experiments.
Monitoring and auditing the agent
Process and network
Use pidstat and nethogs to watch resource spikes. For long-term trends pipe container logs into Loki and set alerts on:
- Outbound connections > 30/min
- Memory usage > 350 MB
- Sudden spike in stderr logs
Prompt and tool invocation logs
OpenClaw writes JSON traces when OPENCLAW_DEBUG=1. Ship them to Elasticsearch:
docker run ... -e OPENCLAW_DEBUG=1 | tee /var/log/openclaw/trace.jsonl
Kibana search examples:
tool:"shell" AND command.keyword:/rm -rf/
Community tip: enable “prompt redaction” to strip e-mail addresses from logs, merged in PR #8612 last month.
Automating policy with AppArmor
If you deploy on Ubuntu, AppArmor beats hand-rolled iptables + sudoers files. Policy snippet:
/usr/bin/node {
# deny everything by default
deny network,
deny @{HOME}/ rwl,
# allow GET/HEAD via libcurl (OpenClaw uses node-fetch)
network inet tcp,
# writable state dir
/srv/claw-state/ rw,
# specific binaries
/usr/local/bin/npm Px,
}
Load it:
apparmor_parser -r -W /etc/apparmor.d/usr.bin.openclaw
I keep policies in Git, reviewed by another engineer before pushing to prod. This tiny review loop caught a “just open port 25 for email tests” commit that would have enabled arbitrary SMTP spam.
When ClawCloud makes more sense
Spending mornings chasing iptables edge cases is fun exactly once. ClawCloud’s hosted runtime already applies:
- Read-only root FS with writable user partition
- Per-agent network ACLs via eBPF
- Automatic API credential vaulting
- Signed audit logs stored 30 days
I still run local containers for rapid prototyping, but production-facing Slack agents moved to ClawCloud after two weeks. Cost was lower than the time I burned tuning AppArmor.
Next step: apply it to your agent today
Pick the first permission you can revoke without breaking your workflow — probably POST requests or write access to $HOME. Measure the fallout, patch, and move down the roadmap. The sooner you start the lock-down, the cheaper every future incident will be.