5 minutes
Running a Personal AI Agent on Cloudflare
I wanted a personal AI Agent that I could reach through my chat app in my mobile, use through the native dashboard when I wanted a browser interface, and connect to model providers with my own credentials. I already use Cloudflare OS for my daily work and I need something more personal for me outside of work.
That is why I put Hermes in a Cloudflare Sandbox container and put a Worker in front of it. The result is hermesworkers: an experimental, single-tenant deployment for one personal agent.
Project attribution: This project is a fork of PlaydaDev/hermesworkers, which is licensed under Apache License 2.0. PlaydaDev’s project credits Nous Research for Hermes and Cloudflare’s Sandbox SDK and moltworker reference implementation.
That scope is intentional. I was not building a shared agent platform or a general-purpose chat service. I needed a small system with a clear request path, a place for Hermes to keep its state, and an explicit boundary around access and provider credentials.
The Design I Chose
The Worker is the public front door, not the agent. It receives HTTP requests, serves the OpenAI-compatible API at /v1/chat/completions, and can proxy the native Hermes dashboard from an optional hostname. A Durable Object named HermesInstance owns the Cloudflare Sandbox container where Hermes runs.
┌──────────────────────────────────────────────────────┐
request │ Cloudflare Worker ( src/index.ts ) │
─────────► │ ├─ /api/health, /v1/chat/completions, /api/... │
│ └─ optional dashboard hostname proxy │
└─────────┬────────────────────────────┬───────────────┘
│ Sandbox SDK │
│ containerFetch │ startProcess
▼ ▼
┌──────────────────────────────────────────────────────┐
│ Durable Object: HermesInstance │
│ └─ Cloudflare Sandbox container │
│ ├─ port 18789 → Hermes API server │
│ └─ port 9119 → Hermes native dashboard (web) │
└──────────────────────────────────────────────────────┘
Cloudflare does not replace Hermes in this design. Hermes remains the process that serves the agent API and dashboard. The Worker handles the public HTTP boundary. The Durable Object handles the relationship with the container. The container is where the Hermes process and its writable state live.
This split made the system easier for me to reason about. Public request handling stays separate from the agent runtime, and the lifecycle decision stays in one place instead of being spread across request handlers.
State Was the Constraint
An agent is not useful to me if each restart makes it forget its working context and local configuration. The container can sleep after inactivity, but Hermes state lives under ~/.hermes/ in the container. The project persists that state across Sandbox sleeps through snapshot behaviour.
That is the specific trade-off I wanted: the process does not have to run continuously, while its sessions, crons, and cached skills do not have to disappear when it stops. The README estimates an initial wake-up at 10 to 30 seconds, and later wakes at a few seconds. That wait is part of the design, not a failure mode I am trying to hide.
I will cover the lifecycle mechanics in more detail later in this series. At this stage, the important decision was to accept a wake-up path in exchange for not keeping a personal agent process running without a request.
Keeping Provider Keys Out of the Image
I also wanted to use my own provider keys without embedding them in the container image or the Worker code. hermesworkers uses Cloudflare secrets, injects them into the Hermes startup process, and has that process write the Hermes environment file at boot.
The Worker does not persist the provider keys or inspect the chat message content. That is a useful boundary for this deployment: the public entry point routes the request, while the container process receives the configuration it needs to call the selected provider.
The details of provider configuration deserve their own post. The design choice here was simply to keep credentials as deployment configuration rather than application source.
Using AI Gateway and Workers AI
I also included Cloudflare AI Gateway in this project so that Hermes inference calls, including chat and the agent’s auxiliary tasks, can be routed through a single gateway endpoint. The project supports both AI Gateway pass-through mode and its REST API and Unified Billing mode. In either case, the startup configuration supplies the gateway URL and inference token to Hermes rather than placing them in the Worker source or image.
For models that run on Workers AI, hermesworkers supports the @cf/author/model form. Those requests require the AI Gateway ID header, which the startup configuration provides through Hermes’s custom OpenAI headers. This gives the project a Cloudflare-native model option alongside the external providers it can use through the same agent interface.
A Deliberately Narrow Starting Point
There are broader designs I could have pursued: multiple users, long-running infrastructure, or a custom web interface. None of those were requirements for my personal agent. Starting with a single tenant let me make the lifecycle, state, and authentication model explicit before adding complexity.
The next post explains the request path in more detail: what the Worker does, what the Durable Object does, and where Hermes starts doing the work.
Sources
- hermesworkers README: Architecture
- hermesworkers README: What is hermesworkers?
- hermesworkers README: Container lifecycle
- hermesworkers README: Container cost estimate
- hermesworkers BYOK setup: Using Cloudflare AI Gateway
- Cloudflare AI Gateway documentation
- Cloudflare Workers AI documentation
- Hermes Agent repository
- PlaydaDev hermesworkers