Building a Two-Agent Personal Cloud on Coolify and Hetzner

How I deployed OpenClaw for research and Hermes for engineering as separate agents on a single Hetzner server, coordinated them through authenticated Agent Mail, and wired the result to Vercel deployment.

8/13/2026

Two agentsCoolify on HetznerAgent Mail coordination

I needed two different assistants, but I did not want two different laptops.

One assistant does research: it lives in Telegram, it can search the web through Brave, and it reports back with sources. I call it OpenClaw. The other does engineering: it works through a headless OpenCode instance, reads repositories, edits files, and runs tests. I call it Hermes.

Both run on the same Hetzner server, managed through Coolify, and coordinated through a private mailbox system I set up at agentmail.rajeevg.com. When research completes, OpenClaw can drop a message for Hermes. When engineering finishes, Hermes can report back.

This is how that system actually works, what I proved, what remains unfinished, and what I learned about running personal AI infrastructure without creating a maintenance burden.

The short version

OpenClaw (Research)
Telegram + Brave
Receives messages via Telegram, searches with Brave, returns summaries with citations.
Hermes (Engineering)
Headless OpenCode
Runs headless OpenCode, edits files, runs tests, pushes commits. Accepts both direct Telegram requests and Agent Mail briefs.
Coordination
Agent Mail
Authenticated MCP at agentmail.rajeevg.com, five-minute polling, acknowledgements.
Output
rajeevg.com
Writable deploy key, real Git push, Vercel CLI identity verified.

The entire setup lives on a single Hetzner instance running Coolify. The agents do not run on my laptop. They run on the server, stay running when I close my laptop, and coordinate through the mailbox.

Why separate agents instead of one

I tried the single-agent approach first. One assistant that does research and engineering both. The problem is not capability. It is context.

Research wants broad search, loose constraints, exploratory queries. Engineering wants tight file access, specific commands, careful version control. When one agent tries to do both, the research context bleeds into the engineering session, and vice versa.

Separating them means:

  • OpenClaw can run with broader permissions for web search, without those permissions following into the engineering workspace
  • Hermes can have repository access and Git credentials, without those being available to the research side
  • Each can restart, update, or fail without taking the other down
  • The mailbox creates an explicit handoff: research produces a message, engineering consumes it

This is not about having more agents. It is about having clearer boundaries.

The infrastructure: Coolify on Hetzner

Coolify is an open-source platform that manages applications, databases, and services on your own servers. It sits between raw infrastructure and the applications, handling deployment, environment variables, and basic networking.

The Hetzner instance has 2 vCPU and about 3.7 GiB RAM, located in Germany. This is not high-end hardware. It is enough for two agents because each is relatively light when idle and scales up only when working.

What Coolify actually manages

Each agent is a service in Coolify:

  • OpenClaw: A Node.js application, Telegram bot polling, Brave search integration
  • Hermes: A Docker container with headless OpenCode, git, Node.js, and Python tooling
  • Agent Mail: The Dicklesworthstone MCP Agent Mail service, handling message queues between agents

Coolify handles the reverse proxy, SSL certificates through Let's Encrypt, and environment variable injection. The web interface is accessible over HTTPS, authenticated with a strong password.

What was proven about access

I verified these access controls work:

  • HTTPS Coolify access: The Coolify dashboard is reachable only via HTTPS; the legacy public port 8000 is blocked
  • Port 8000 blocked externally: The Coolify internal port is not exposed to the public internet
  • UFW restricted: Uncomplicated Firewall allows only ports 22 (SSH), 80 (HTTP redirect), and 443 (HTTPS)
$ sudo ufw status
Status: active
 
To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere

This is basic hygiene, but I verified it rather than assuming.

The agents in detail

OpenClaw: Research via Telegram

OpenClaw is the official OpenClaw gateway/image using Telegram long polling and the official Brave plugin. It receives messages, decides whether it needs search, calls the Brave Search API, and returns formatted responses.

Key characteristics:

  • Telegram ID allowlist: Only specific Telegram user IDs can interact with the bot. This is enforced in code, not just by keeping the bot handle private
  • Brave Search: Web searches go through Brave's API, which returns results without the tracking baggage of other search providers
  • Stateful threads: The bot maintains conversation context per chat, so follow-up questions work naturally

The allowlist matters because Telegram bots are discoverable. Even if you do not publish the handle, brute-force discovery is possible. The allowlist rejects messages from unknown IDs silently.

Hermes: Engineering via headless OpenCode

Hermes runs headless OpenCode, never Codex, and accepts both direct Telegram requests and Agent Mail briefs. This means:

  • It can work with repositories, edit files, run tests, and make commits
  • It does not need a GUI or browser session
  • It responds to messages from the mailbox rather than direct user input

The container includes:

  • Git with a writable deploy key for rajeevg.com
  • Node.js and pnpm for the Next.js site
  • Python for utility scripts
  • OpenCode CLI configured for headless operation

The deploy key is scoped: it can push to the repository, but it cannot access other repositories or administrative settings. It is a deployment credential, not a personal access token.

Agent Mail: The coordination layer

Both agents connect to the same mailbox service running at agentmail.rajeevg.com. This is an authenticated MCP (Model Context Protocol) service based on Dicklesworthstone's MCP Agent Mail.

How the mailbox works

  • Authenticated: Each agent authenticates with a token. Unauthenticated requests are rejected
  • Namespaced: Messages are addressed to specific agents (e.g., "hermes", "openclaw", "amberowl", "silverforge")
  • Acknowledged: When a message is consumed, the mailbox sends an acknowledgement back to the sender
  • Replies: Agents can reply to messages, creating threaded conversations
  • Five-minute polling: Agents poll the mailbox every five minutes when idle, checking for new work

The polling interval is a tradeoff. Shorter intervals mean faster response but more server load. Five minutes is a reasonable middle ground for personal use where immediate response is not required.

AmberOwl and SilverForge

The mailbox supports more than two agents. I have defined four:

  • OpenClaw: Research agent
  • Hermes: Engineering agent
  • AmberOwl: Planning and review agent (currently minimal use)
  • SilverForge: Testing and validation agent (currently minimal use)

For now, most traffic is between OpenClaw and Hermes. The other two are placeholders for future separation of concerns.

Deployment: Git push to Vercel

When Hermes completes work on the rajeevg.com repository, it pushes commits using the writable deploy key. Vercel picks up the push and deploys.

What was verified about deployment

  • Vercel CLI identity: I verified that the Vercel CLI recognizes the deployment context and can check status
  • Real Git push workflow: Commits pushed by Hermes trigger actual Vercel builds, not just webhook simulations
  • Build success: The pushed code builds successfully on Vercel's infrastructure

The deploy key is configured in the repository settings, not in my personal SSH config. This means Hermes can push without having my personal credentials.

Security boundaries

The setup has several deliberate security boundaries:

BoundaryImplementation
Agent isolationSeparate containers, separate credentials, communicate only through mailbox
Network accessUFW restricts to 22/80/443, internal ports not exposed
Repository accessDeploy key is repository-scoped, not account-scoped
Telegram accessAllowlist rejects unknown user IDs
Mailbox authenticationToken-based, agents cannot impersonate each other
HTTPS onlyCoolify enforces SSL, no plain HTTP access

No security setup is perfect. These boundaries reduce risk, not eliminate it.

What remains pending

I am being explicit about what is not yet done:

  • Gmail OAuth: Pending completion of personal OAuth consent and cloud import. Until then, email alerts do not work.
  • Cloudflare API token: Pending creation and provisioning of a scoped API token for DNS management. Until then, DNS changes require manual updates.

These are not hypothetical future features. They are planned but require setup steps that are not yet complete.

Usage patterns

A typical workflow looks like:

  1. I send a research question to OpenClaw via Telegram
  2. OpenClaw searches Brave, compiles an answer, and optionally drops a message for Hermes if implementation is needed
  3. Hermes polls the mailbox, sees the message, pulls the repository, makes changes
  4. Hermes commits and pushes; Vercel deploys
  5. OpenClaw receives acknowledgement that Hermes picked up the task
  6. I receive a Telegram message when research completes and when deployment finishes

The agents do not replace my judgment. They handle the mechanical parts: searching, editing, committing, deploying. I still review the research and the code before considering it done.

Maintenance realities

Running your own agents is not free. It costs time as well as money.

What I monitor

  • Coolify dashboard: Service health, memory usage, disk space
  • Mailbox queue depth: If messages pile up, something is stuck
  • Vercel build logs: Deployment failures are visible there
  • Hetzner console: If all else fails, raw server access

What I update

  • Container base images monthly
  • OpenCode when new versions release
  • Node.js dependencies when security advisories appear
  • Telegram bot framework when updates are available

The agents are not "set and forget." They need the same maintenance attention as any other service.

Backup considerations

The Agent Mail service uses persistent storage. No daily object-storage backup was configured because the owner explicitly accepted clean rebuilds.

Lessons learned

1. Separation of concerns beats clever integration

I initially tried to make one agent that could do research and engineering. It was technically possible but practically messy. The separation into OpenClaw and Hermes, with explicit handoffs through the mailbox, is simpler to reason about and safer to operate.

2. Polling is fine for personal use

I considered webhooks, sockets, and push notifications. Polling every five minutes is simpler, more reliable, and sufficient for my use case. The complexity of real-time coordination was not worth the benefit.

3. Verification matters more than configuration

I configured UFW, but I also ran ufw status to verify. I set up HTTPS and verified the Coolify hostname works over HTTPS. Configuration files lie. Verification catches the lies.

4. Deploy keys are better than personal tokens

A personal access token would have been easier to set up. It would also have given the agent access to every repository I can touch. The deploy key is more work but limits blast radius if the key leaks.

5. External dependencies take time

The Gmail OAuth and Cloudflare token require setup steps that are not yet complete. I cannot rush these steps. The lesson is to build without them first, then add them when ready, rather than waiting to launch.

Cost and performance

The Hetzner instance costs a few euros per month. Coolify is free and open source. Telegram bot API is free. Vercel hobby tier covers the deployment.

Total running cost: a few euros per month, plus my time for maintenance.

Performance is acceptable for personal use. Engineering tasks vary by complexity. Neither agent is fast, but both are asynchronous: I send a request, do other work, and receive a notification when ready.

What this proves and what it does not

Verified
  • HTTPS Coolify access working
  • Port 8000 blocked externally
  • UFW configured for 22/80/443 only
  • Telegram ID allowlist enforced
  • Agent Mail message acknowledgements and replies
  • Five-minute polling operational
  • OpenCode repo and generation smoke tests pass
  • Vercel CLI identity confirmed
  • Real Git push workflow end-to-end
Pending
  • Gmail OAuth consent and cloud import
  • Cloudflare API token creation
  • Load testing under concurrent agents

This is a working personal system, not a production service for others. It works for my use case. It would need more work to work for yours.

The shape of the work

If you want to build something similar, the sequence that worked for me:

  1. Get the server: Hetzner, DigitalOcean, or similar with sufficient RAM for your workload.
  2. Install Coolify: Follow their documentation. It handles the heavy lifting of reverse proxy and SSL.
  3. Deploy one agent: Start with whichever matters more to you. Get it working alone.
  4. Add the mailbox: Get two simple scripts to pass messages through it.
  5. Add the second agent: Connect it to the mailbox, verify handoffs.
  6. Harden: Add allowlists, firewalls, scoped credentials.
  7. Verify: Try to break it. Check that boundaries hold.

The temptation is to build both agents at once, or to skip verification and trust configuration. Both temptations lead to debugging at 2am when something subtle breaks.

The honest bottom line

I have two agents now. They run on a server I control, they coordinate through a mailbox I own, and they deploy to a site I built. They are not magic. They are careful plumbing: clear boundaries, explicit handoffs, verified constraints.

The real value is not having AI agents. It is having agents that stay running, stay separate, and stay honest about what they can and cannot do.

The Gmail OAuth and Cloudflare token will be ready when the setup steps are complete. Until then, the system works without them. That is the point: build what you can verify, ship what you can maintain, and be honest about what remains pending.