You landed here because you typed “how to secure OpenClaw” into a search bar. Same. Last month I migrated three production agents from a pet server under my desk to a public VPS and needed a no-nonsense hardening recipe. I couldn’t find one, so I wrote the steps down while they were still fresh. Everything below has been run on Ubuntu 22.04 LTS with OpenClaw v2.9.1 (Node 22.2.0). The checklist format is intentional—copy, paste, adapt.

Skill prerequisites and threat model

You don’t need to be a security engineer, but you should:

  • Know how to ssh into a box and edit files with nano or vim.
  • Understand basic UNIX permissions (chmod, chown).
  • Be comfortable restarting a systemd service or a Docker container.

The goal is to keep an attacker who compromises an agent prompt from pivoting into the rest of the machine—or worse, the rest of your infrastructure.

Use a dedicated non-root user

OpenClaw does not need root. Running the gateway as root multiplies the blast radius if the LLM hallucinates a malicious shell command.

  1. Create a system account:
sudo adduser --system --group --home /opt/openclaw oc
  1. Move your install tree:
sudo mv /path/where/you/extracted/openclaw /opt/openclaw/gateway sudo chown -R oc:oc /opt/openclaw
  1. Systemd unit (save as /etc/systemd/system/openclaw.service):
[Unit] Description=OpenClaw Gateway After=network.target [Service] Type=simple User=oc Group=oc WorkingDirectory=/opt/openclaw/gateway ExecStart=/usr/bin/node --no-warnings index.js Restart=always [Install] WantedBy=multi-user.target
  1. Enable and start:
sudo systemctl daemon-reload sudo systemctl enable --now openclaw

If you only do one thing on this page, do this.

Read-only file access for messages and memory

By default the gateway writes everything under its working directory. We can leverage UNIX ACLs to make most of the tree read-only and keep only the dynamic bits writable.

  1. Create writable sub-dirs:
sudo -u oc mkdir -p /opt/openclaw/gateway/{logs,memory,tmp}
  1. Lock the rest:
sudo chown -R root:root /opt/openclaw/gateway sudo chmod -R 755 /opt/openclaw/gateway sudo setfacl -m u:oc:rwx /opt/openclaw/gateway/logs sudo setfacl -m u:oc:rwx /opt/openclaw/gateway/memory sudo setfacl -m u:oc:rwx /opt/openclaw/gateway/tmp

The agent can still mutate memory and temporary files, but any attempted code write or dependency injection fails fast.

Container isolation with Docker (optional but nice)

If you prefer reproducibility over raw performance, wrap the whole thing in a minimal container. The official image is ghcr.io/openclaw/openclaw:2.9.1, but rolling your own means you can drop Linux capabilities you don’t need.

Minimal Dockerfile

FROM node:22-alpine as base RUN apk add --no-cache dumb-init WORKDIR /app COPY . ./ RUN npm ci --omit=dev && npm cache clean --force EXPOSE 3000 USER node ENTRYPOINT ["/usr/bin/dumb-init", "node", "index.js"]

docker-compose.yml with hardening flags

version: "3.9" services: openclaw: build: . restart: always ports: - "127.0.0.1:3000:3000" # front with nginx, no direct WAN read_only: true tmpfs: - /tmp cap_drop: - ALL security_opt: - no-new-privileges:true environment: - NODE_ENV=production

The read_only: true flag pairs with a tmpfs mount so you still get /tmp. No capabilities, no problem. I run this behind nginx with mutual-TLS to keep bots out of the gateway port.

Run scanner.py after every deploy

OpenClaw ships with an opinionated audit script under tools/scanner.py. It inspects:

  • Stale dev dependencies
  • Writable files in code paths
  • Missing .env vars marked as required
  • Integration tokens with overly broad scopes (via the Composio API)

Usage

cd /opt/openclaw/gateway python3 tools/scanner.py --output report.html

Read the HTML report in a browser, fix the reds, re-run until green. The script exits non-zero if any criticals remain, so you can wire it into CI:

- name: OpenClaw audit run: python3 tools/scanner.py --output /tmp/report.html

We have a GitHub Action openclaw/audit@v1 internally that does this on pull request—but the plain script works anywhere.

Install the “vaccine” memory plug-in

Memory injection is the OpenClaw feature that scares most ops people: an LLM can store arbitrary strings that later get executed via shell or forwarded to third-party tools. The core team ships @openclaw/memory-vaccine, a policies-first drop-in that whitelists schema, strips executable code, and enforces size limits.

Installation

# still as oc user yarn add @openclaw/memory-vaccine

Gateway config snippet

// gateway.config.js module.exports = { … memory: { driver: "vaccine", limit: "1MB", blockPatterns: ["/\\b(rm|unlink|curl|wget)\\b/"], allowHTML: false } };

This blocks path traversal attempts that started showing up in the public demo channels last week (tracked as GHSA-claw-2024-04).

Lock down direct-message pairing policies

Every integration (Slack, Discord, Telegram, WhatsApp) has its own idea of “DM”. Agents that respond to anyone with the link are great for demos but terrible for prod.

  1. Create an allowlist:
openclaw gateway dm-pairing --allow user:U12345 --allow role:admin --deny-by-default
  1. Audit once a week:
openclaw gateway dm-pairing --list | grep -v "allowed" && \ echo "⚠️ unexpected users have DM access"

Slack Enterprise Grid users can merge this with SCIM to auto-revoke employees on exit. Community users on GitHub started a script for Telegram Chat IDs here.

Run openclaw doctor after patching

The CLI now ships a diagnostic command inspired by brew doctor. It checks:

  • Node.js version >= 22
  • Environment variables present and non-empty
  • File permissions on known sensitive paths
  • Whether you’re on the latest patch release

Sample output

$ openclaw doctor ✔ Node 22.2.0 — good ✔ .env loaded — 17 vars ✖ Writable file detected: /opt/openclaw/gateway/index.js ✖ Version 2.9.1 — 2.9.3 available 2 issues found — run 'openclaw doctor --fix' to auto-patch

I run doctor in a daily cron and send anything non-zero to PagerDuty:

0 3 * * * /usr/local/bin/openclaw doctor --quiet || cat /var/log/openclaw/doctor.log | mail -s "OpenClaw issues" ops@company.com

The quick-glance checklist

Print this, tape it near your monitor:

  • [ ] Dedicated oc user, no root
  • [ ] Read-only app tree with writable logs/ memory/ tmp/
  • [ ] Docker container with cap_drop: ALL and read_only: true (if you use Docker)
  • [ ] Passes scanner.py with 0 criticals
  • [ ] memory-vaccine module enabled, 1 MB cap
  • [ ] DM pairing allowlist, default deny
  • [ ] Daily openclaw doctor cron

Next step: automate it

Humans forget. Machines don’t. Throw the above into Terraform, Ansible, or a GitHub Actions workflow so new nodes boot hardened by default. If you publish your scripts, drop a link on the #security channel in the OpenClaw Discord—there’s always someone testing the edge cases.