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/bash when 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.

  1. Research-only mode
    Read-only FS, GET-only network.
  2. Stateful mode
    Add a writable /data volume for memory.
  3. Approved shell snippets
    Whitelisted sudo commands or custom tool.
  4. GET + specific POST targets
    Allow POST only to domains you own (e.g., internal webhook).
  5. SaaS API keys with scoped tokens
    Give GitHub PAT with public_repo only, Gmail restricted to a label, etc.
  6. Outbound email / messaging
    Enable Composio integrations that can reach users.
  7. Full local shell
    Only if the agent needs to compile or deploy.
  8. 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.