Why I moved my family scheduler off Moltbot

cloudflarecontainersdurable-objectsagentsfamsynclab-note

The first version of my family scheduler was, on paper, the cool one. It was a real autonomous agent — a full assistant CLI with a shell, a browser, and persistent memory — running in a container in the cloud. It worked. It was also expensive to run, a pain to build, and slow to wake up — and for a bot that spends most of the day idle, those three problems added up to the wrong tool for the job. Here’s what I built, why it fought me, and why the rebuild has no container at all.

What I built

I started from Cloudflare’s moltworker proof of concept: run OpenClaw — the personal AI assistant formerly known as Moltbot — inside a Cloudflare Sandbox container. The shape looked like this:

  • A Durable Object owned a Sandbox container (cloudflare/sandbox:0.7.20).
  • Inside the container: the OpenClaw agent, Node 22, and a gateway process it talked to over an exposed port.
  • R2 held the persistent state, backed up and restored through the Sandbox SDK (mksquashfs snapshots over a FUSE overlay).
  • A cron trigger firing every minute kept the container warm and woke it ahead of scheduled jobs.

The appeal was real: this wasn’t a narrow bot, it was a whole agent that could run commands, browse, and remember. For a while that felt like the future.

Where it broke

Three things did it in — the cost, the cold starts, and the build — and under all of them, a fourth realization about fit.

It cost too much for what it was

A family scheduler is idle almost all the time — a burst of messages at dinner, then nothing for hours. But a warm container bills whether or not anyone’s typing. On a standard-1 instance (½ vCPU, 4 GiB, 8 GB disk) running 24/7, the math came out around:

ResourceApprox. cost
Memory (4 GiB provisioned)~$26/mo
CPU (~10% utilization)~$2/mo
Disk (8 GB)~$1.50/mo
Workers Paid plan$5/mo
Total~$34.50/mo

Thirty-five dollars a month to remember swim practice. I could drop it by letting the container sleep when idle — which led straight to the second problem.

Sleep to save money, pay in cold starts

The moment the container sleeps, the next message has to wake it, restore state from R2, and start the gateway before it can answer. That restore-and-start path is where most of my time went. The git history is basically a monument to it — a long run of fixes for blank pages, CPU-limit trips, gateway-restart races, cross-isolate restore coordination, “don’t start the gateway if the restore timed out,” “eliminate all blocking DO calls from the HTML path.” Every one of those was a real evening spent making a container behave like something that was always on, when the whole point was that it usually wasn’t.

I ran it for about three weeks, and in that time it went down at least twice a week — each outage a message that quietly got no reply. For a bot whose entire job is to not drop things, silently dropping them is the worst way to fail. Three weeks was enough.

The build itself fought me

I work behind a corporate TLS-intercepting proxy, so even building the image meant injecting a corporate CA bundle into the Docker build so npm install and the Node download over curl wouldn’t fail on certificate errors. That’s a .crt file and three Dockerfile lines that exist purely to get past my own network — a smell that I was doing a lot of work to run someone’s laptop assistant in the cloud.

It was the wrong shape for the job

Step back and the task is small: read a WhatsApp message, work out the intent, call the Google Calendar API, reply. I was running a full agent runtime — shell, browser, persistent filesystem, an every-minute heartbeat — to do something that is, at heart, one API call behind one language model. The capability was impressive and almost none of it was load-bearing.

The pivot

So I deleted the container. No Docker, no OpenClaw, no R2 restore dance, no keep-warm cron. The rebuild is just a Worker + a Durable Object holding a short conversation history + Claude driving native tool calls through the AI Gateway. It scales to zero, costs rounding-error pennies, and there is no warm process to babysit — when no one’s texting, nothing is running.

The lesson wasn’t “containers are bad” or “agents are bad.” It was match the architecture to the workload. The most capable option — a full autonomous agent in a container — was the wrong fit for a spiky, mostly-idle, well-defined task. The boring option turned out to be the right one.


The rebuilt, container-free version is live and running my family’s calendar today — here’s how it actually works: How my WhatsApp scheduler actually works →


← all writing