AI agents · 14 min read
OpenClaw: the assistant I actually run
The interesting part of OpenClaw is no longer the repo. It is the live system: a long-lived assistant running on a GCP VM, with Telegram delivery, persistent memory, cron jobs, backups, retries, and exactly the kind of operational messiness that makes software real.
I used to describe OpenClaw as a personal AI assistant for my development workflow. That is still true, but it undersells what the project became once I started depending on it. It is not a chat demo. It is infrastructure I talk to.
The source of truth for this write-up is not a local clone. It is the instance I actually run on a GCP VM called openclaw-gateway. I inspected the live config, the Docker deployment, the cron jobs, the persistent state, the health endpoints, and the task history. That turned out to be the more honest way to write about the project, because production tells a different story than a repository does.
The story the VM tells is useful. OpenClaw is a self-hosted assistant organised around one long-lived gateway process. It owns the chat surface, the tool surface, the scheduling model, the state directory, and the browser control plane. The current live deployment is much narrower than the broader codebase: in practice it is running as a Telegram-first assistant with a web UI, multiple agent personas, and a growing set of scheduled workflows.
The architecture is a gateway, not a bot
The cleanest idea in OpenClaw is that the assistant is not model-first and not channel-first. It is gateway-first.
On the VM, the gateway listens on port 18789. That one process serves the browser UI, the WebSocket control plane, health and readiness endpoints, session state, scheduled jobs, and the connected chat channel. Instead of having a separate backend for the web UI, another daemon for automation, and yet another adapter for messaging, everything routes through the same long-lived process.
Telegram │ ▼ OpenClaw Gateway :18789 │ ├─ sessions ├─ tools ├─ cron ├─ memory ├─ health └─ browser UI / WebSocket clients
That matters because assistants stop being interesting when every feature becomes its own sidecar. I wanted one place where state lives and one place where routing decisions happen. The live instance reflects that design very directly: the gateway is the boundary, and almost everything meaningful sits behind it.
The current production config is also instructive in what it does not do. Only Telegram is enabled right now. The gateway is not pretending to be a universal inbox in daily use; it is a focused personal system that happens to have been built on top of a more general channel-and-tool architecture.
The state model is the real product
The most important directory in the whole deployment is ~/.openclaw. That is where the assistant really lives.
On the VM it contains config, credentials, devices, cron state, logs, agent workspaces, tasks, memory indexes, delivery queues, and session transcripts. The container is almost disposable. The state directory is not.
| path | what it stores |
|---|---|
| ~/.openclaw/openclaw.json | live gateway, model, channel, and agent config |
| ~/.openclaw/agents/ | agent personas and session history |
| ~/.openclaw/memory/ | SQLite-backed memory stores |
| ~/.openclaw/cron/ | scheduled jobs and run history |
| ~/.openclaw/tasks/ | task execution records |
| ~/.openclaw/logs/ | config health and audit logs |
| ~/.openclaw/workspace/ | assistant workspace, notes, scripts, and memory files |
That layout reveals something I like architecturally: OpenClaw is file-first. The running process is important, but the assistant is mostly represented as inspectable state. That makes it easier to back up, easier to migrate, and much easier to debug than a system where half the truth lives in undocumented runtime memory.
Why this design aged well
Memory is not magical - and that is why it works
The live instance uses Gemini-backed semantic memory search, but the part I trust most is more boring than that. Memory is grounded in the workspace and persisted indexes, not in vague promises about what the model will remember next turn.
The VM has separate memory databases under ~/.openclaw/memoryfor different personas: main, coder, devops, lead, and security. The main agent is the one actually carrying current usage, but the shape of the system is already there: different roles, different memory stores, one shared gateway.
I like that split because it matches how I actually use assistants. I do not want one undifferentiated blob of context. I want a general assistant, but I also want separate working modes with different responsibilities. The deployment shows that idea very clearly even if, right now, the main persona is where the real activity is.
There is another practical benefit here: persistent memory can be backed up like any other state. The host runs a nightly cron job that syncs the whole ~/.openclaw tree to a GCS bucket. That is a much more useful memory story than “the agent is stateful” in the abstract.
How it is actually deployed
The production deployment is straightforward in the best possible way. OpenClaw runs in Docker on a Debian GCP VM. Docker Compose defines the gateway container. Docker restart policy keeps it alive. The host bind mounts the persistent state directory into the container.
services:
openclaw-gateway:
build: .
image: openclaw-gateway-custom:latest
restart: always
ports:
- "18789:18789"
volumes:
- /home/<user>/.openclaw:/home/node/.openclaw
- /home/<user>/.config/gcloud:/home/node/.config/gcloud
entrypoint: ["/home/node/.openclaw/entrypoint.sh"]
command: ["node", "openclaw.mjs", "gateway", "--allow-unconfigured"]The running container was created in early May and, at the time I inspected it, had been up for more than two weeks continuously. The image is a custom derivative of the upstream OpenClaw image. It adds a few things the workflows need on the server: GitHub CLI, Python, some data-pipeline libraries, and Codex CLI.
There is a small but telling detail in the custom entrypoint: it symlinks Codex state into the mounted OpenClaw state directory so auth and runtime state survive container recreation. That is the kind of production adaptation I like. It is not flashy, but it is exactly how tools stop feeling ephemeral.
How it is exposed
There is no nginx or caddy in front of the live system. Docker publishes port 18789 directly on all interfaces, and the gateway itself enforces token authentication.
That means the gateway is doing triple duty: application server, control plane, and security boundary. The live config has gateway.bind = "lan" and gateway.auth.mode = "token", which is the right combination for this setup, but it is still a deliberate tradeoff. Simplicity won over layering.
Tailscale is also installed and running on the host, which gives me a private remote-access path when I want one. So the system has two real access patterns: direct gateway access with token auth, and private network access through Tailscale. That combination makes sense for a personal tool that I still want to reach from multiple places.
The health model is intentionally simple. The gateway serves /healthz and /readyz, both live on the same port as the UI. At the moment I inspected it, both were returning 200 and Docker marked the container healthy.
Cron is where this stopped being a toy
The strongest evidence that OpenClaw is a real working system is its scheduler.
The live instance has 13 internal cron jobs stored under ~/.openclaw/cron. They cover reminders, summaries, health-checking, token refresh, finance ingestion, memory distillation, calendar briefings, a daily session log, and a job monitor. Most run as isolated agent turns rather than as main-session nudges, which is exactly the right choice for noisy automation.
| job | schedule | delivery |
|---|---|---|
| Morning tech news summary | 08:30 daily | announce |
| Calendar pre-event prep briefing | 07:00 daily | none |
| Calendar post-event debrief prompt + CRM capture | 20:30 daily | none |
| Refresh Google Calendar token | every 50 minutes | none |
| Daily food diary prompt | 20:00 daily | announce:last |
| Daily IBKR portfolio fetch | 18:00 weekdays | announce:last |
| Weekly memory distillation | 09:00 Mondays | announce:last |
| Water the plants reminder | 09:00 Wed/Sat | announce |
That list says more about the project than any architecture diagram could. OpenClaw is not just answering messages. It is managing a recurring personal workflow: calendar hygiene, finance chores, health checks, journaling, reminders, and maintenance.
There are also two host-level cron jobs outside the gateway itself. One backs up the whole state directory to Google Cloud Storage at 02:00 every night. The other runs a weekly Telegram session-trimming script to prevent duplicate-message issues. I am especially fond of that second one because it is so operationally honest. Real systems accrete janitor scripts.
The failures are as informative as the successes
One reason I wanted to inspect the live task database is that a health endpoint only tells you whether the process is breathing. It does not tell you whether the assistant is actually doing useful work.
In the last week, the task database recorded about 350 scheduled task executions. A large share of those were retries for the Google Calendar token refresh job, and many of them failed. Several announce-style jobs also failed because delivery routing was not cleanly resolved. In other words: the system is very much alive, but parts of the automation layer are brittle.
Last 7 days of task runs - Refresh Google Calendar token: 303 runs, many failures - Morning tech news summary: 7 failures - Daily session log: 7 failures - Daily food diary prompt: 7 failures - Calendar pre-event prep briefing: mixed success/failure - Calendar post-event debrief prompt + CRM capture: mixed success/failure
I like that I can say that plainly. This is exactly the sort of thing I want from a personal assistant write-up: not “it has cron,” but “the cron layer surfaces real reliability problems once you actually depend on it.”
The operational lesson
The current shape of the assistant
The live config currently defines five agent personas: main, lead, coder, security, and devops. All of them default to the same primary model family right now, with fallbacks configured across providers, but only the main agent is showing meaningful day-to-day activity.
That tells me something useful about the maturity of the system. OpenClaw has already crossed the line from “one chat session” to “assistant platform with roles,” but the operational centre of gravity is still a single personal mainline. That feels right. I would much rather grow a working personal system outward than invent a multi-agent architecture before I need it.
It also explains why the project later influenced work I did professionally. Once you have a live gateway that owns state, tools, retries, personas, and scheduling, you stop thinking of an assistant as a prompt and start thinking of it as a runtime.
What I would change
The first thing I would change is reliability instrumentation. The system already records task runs, and that is good, but the live deployment would benefit from much clearer job-level success signals and failure summaries. Right now I can inspect the truth, but I still have to go looking for it.
The second is ingress discipline. Publishing the gateway directly keeps the deployment wonderfully small, but it also concentrates a lot of responsibility in one process. I still like the simplicity, but I would probably want a more explicit edge story if I pushed this further.
Third, I would make the separation between “platform state” and “personal workspace state” cleaner. ~/.openclaw is a very effective home for everything, but it is also where years of habits, experiments, and production concerns pile up together. It is manageable because it is mine. It would be less pleasant as a generic product boundary.
And finally, I would keep writing about the live system rather than the abstract one. The deployed OpenClaw is narrower, messier, and more interesting than the broader clone on disk. That is a good reminder that software is most truthfully described where it actually runs.
The stack
- Host: Debian VM on GCP, with Docker and Tailscale.
- Runtime: OpenClaw 2026.4.14, Node 24 inside the container.
- Deployment: Docker Compose, one gateway container, restart policy
always, direct port publication on 18789. - State: Persistent bind-mounted
~/.openclawdirectory with config, sessions, memory, cron, tasks, and logs. - Channel in current use: Telegram with pairing / allowlist policy.
- Automation: 13 internal cron jobs plus host cron for backup and maintenance.
- Memory: Persistent per-agent memory stores with Gemini-based semantic search configured on the live system.
OpenClaw matters to me because it graduated from “assistant idea” to “assistant I have to operate.” That is where the real engineering starts. The repo still matters, but the VM is where the project became honest: a gateway, a state directory, a scheduler, a chat surface, a backup job, a maintenance script, and a long list of small tradeoffs that together make the thing useful.