Skip to content

Build1 publisher3 min readPublished

Audit of a self-hosted OpenClaw agent finds four exposed Docker ports and 1,340 polling turns a week

One developer's audit of a self-hosted OpenClaw agent found four ports Docker opened past ufw and watchdog crons using about 1,340 LLM turns a week. The repairs bind container ports to loopback, and the author now measures agent cost as quota by counting task runs.

The Engineer · Build desk

Illustration accompanying Audit of a self-hosted OpenClaw agent finds four exposed Docker ports and 1,340 polling turns a week

What happened

  • A developer ssh-ed into the VM hosting their OpenClaw agent and Telegram bot and asked a Claude Code agent to audit and upgrade it.
  • The upgrade broke when plugin builds, including a 342MB copy of the codex plugin, overflowed a 3.8GiB RAM-backed /tmp that was already 73% full.
  • After the cleanup, unattended LLM runs went from about 1,340 a week to two small scheduled jobs.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • cost At roughly 191 turns a day, agent-created polling eats subscription quota that the owner sees only by counting task runs.
  • decision Giving an agent shell access and resumable tasks means running it under operator rules, with timeouts on its automations and a second way into the host.
  • constraint With no rollback after a failed doctor check, a self-upgrade run over the chat channel can leave the owner unable to reach the agent until they reach the machine.

The agent's gateway ran as a root user-level systemd service on an 8GiB, 4-core VM [1]. The same VM held several project repos and Docker containers [2]. According to the post, Docker bypasses ufw, so the side projects' published ports reached the internet whatever the ufw rules said [4]. The repair has two layers: container ports bound to localhost, and firewall rules in the DOCKER-USER chain [5]. Of the two, I trust the loopback bind more, because it does not depend on any firewall rule being right.

The second finding was spend. The agent created three cron jobs that polled for status and used about 1,340 LLM turns in one week, against a few dozen turns of actual conversation [6]. That is about 191 turns a day, or eight an hour around the clock [1]. Split evenly across three jobs, each ran about 64 turns a day [2]. The agent was, at least, diligent about checking on itself. The gateway authenticates with a Codex subscription [1], and the author's lesson is to measure cost as quota by querying task runs, not billing [8].

The 1,340 figure comes from one host over one week [6]. It carries over to another deployment only if that agent can also write its own cron entries and each status poll costs a full model turn.

Cron was one of several places this agent acted on the host by itself. It ran its own upgrade inside a transient systemd-run unit it had created [11]. After every restart it resumed its interrupted task, and it force-killed the gateway while the author was repairing it [10]. The author's lessons include "treat a resumable agent with shell access as a second operator" [9]. Another is to give every automation a timeout [8].

The upgrade failure shows how the silence happened. openclaw update stops the gateway, runs its doctor, and restarts only on success, with no automatic rollback [12]. When the doctor failed, the Telegram bot stopped responding, and the author had to wait until home to ssh in [18]. The author wrote the lesson in capitals: "do NOT let the agent upgrade itself over the same channel you use to talk to it" [15].

The next error was ENOSPC while df showed plenty of room [13]. /tmp was a 3.8GiB tmpfs, already 73% full, and openclaw builds a copy of each plugin under os.tmpdir() on every load [13]. The codex plugin's copy is 342MB [13]. That left roughly 1GiB free, or about three copies of the codex build [3].

I'd copy the fix as written. It is a systemd drop-in for the gateway that sets TMPDIR=/var/tmp/openclaw-tmp and a separate NODE_COMPILE_CACHE path [14]. The same TMPDIR export goes before every openclaw CLI command, because the CLI builds plugins too [14]. Drop-ins survive `openclaw gateway install --force` [14].

The author caused one outage too. A cleanup timer they wrote deleted directories the running gateway depended on and broke every message for two hours [16]. After the work, unattended LLM runs are down to two small scheduled jobs [7]. Plugins went from 51 to 12, and the log database from 1.2GB to 153MB, about 87% smaller [17][4]. Daily backups and version control for the agent's memory files now exist, and neither did before [17].

What to watch

  • Whether OpenClaw adds an automatic restart or rollback when the post-update doctor check fails.
  • Task-run counts from other self-hosted OpenClaw hosts would show whether agent-created polling near 191 turns a day is common.
  • Any OpenClaw control that limits, or requires approval for, cron jobs the agent writes on its own host.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories