I Gave My Homelab a Brain

It started as a place to dump my photos. Now a handful of AI agents run it, coach my training, and share one memory. Mostly I just read the texts.

·7 min read ·homelabagentsself-hosted

A year ago my homelab was a shelf of the usual self-hosted stuff: the photos I’d finally rescued off iCloud, a music server, a password manager. Useful, and a little boring. I’d mostly forgotten it was there.

Then I started bolting agents onto it, and somewhere along the way it grew a brain. It texts me now. It reviews my startup’s code while I sleep. It remembers things I told a completely different agent, on my laptop, last week. I never sat down and designed this. It piled up one weekend at a time, and it’s turned into the most fun I’ve had with computers in years. So I want to walk you through how it actually works.

The hardware is boring on purpose

Three machines, none of them special:

  • dom is a used Lenovo ThinkCentre, the little one-litre box that hides behind a monitor. Four cores, 16 GB of RAM, runs everything in Docker, and never turns off.
  • A Raspberry Pi 4 does one job, network-wide DNS, from a shelf over WiFi.
  • fugaku is my gaming desktop (an RTX 3070), switched off most of the time. When it’s awake it’s the GPU for my photo library (face-spotting, instant search), and when it’s asleep that work falls back to dom’s CPU, so my phone keeps backing up either way.

They find each other over a Tailscale mesh, and not a single port is open to the internet. Everything public goes out through a Cloudflare Tunnel. The metal is the least interesting part of this whole story, which is exactly how I wanted it. The good part is everything bolted on top.

Two agents, Claude Code on my laptop and Hermes on the server, both read and write one shared memory graph on dom, which texts me over Telegram.Claude Codelaptop · drives dom over SSHHermes + skillson dom · the agent fleetTelegramit texts meshared memoryGraphiti · FalkorDB · local embeddingsread · writeread · writedom, blueberrypi and fugaku on a Tailscale mesh · no open ports
One memory, two agents. The coding agent on my laptop and the fleet on the server share a single self-hosted knowledge graph, so what one of them learns, the other can recall.

My homelab texts me

The fun started the day I stopped thinking of the server as a box and started treating it like a coworker.

On dom I run Hermes, an open-source agent runtime from Nous Research, and I’ve written a small pile of custom skills on top of it. Each skill is an agent with a job, a schedule, and a Telegram chat it talks to me through. A few that have earned their keep:

  • A workout coach. It reads my training log, keeps my plan on my Google Calendar, and every morning pulls my recovery score from Whoop. On Sundays it does a review and tells me the thing I least want to hear: whether I’m under-recovered, or just under-training and making excuses.
  • A weekly reflection agent. Saturday it reviews the week from my notes (a plain-markdown Obsidian vault), and Sunday it helps me plan the next one. Then it quietly gardens the vault: finding orphaned notes, suggesting links between ideas I’d forgotten were related. It doesn’t journal for me. It just keeps the shoebox tidy.
  • A nightly code reviewer for my startup. It picks whichever of our repos saw the most action that day, reads through the commits, and leaves me a one-line verdict over Telegram with the full notes written to a file. Anything that smells like a security problem gets a 🚨 so it’s the first thing I see.

The morning brief lands on my phone looking something like this:

Hermes · morning brief · 06:30
recovery 64%, HRV 48 ms, slept 7h02
today: lower body, squat 5×5 (you hit 92.5 kg last week)
recovery's middling, so keep the working sets but drop the
last AMRAP if the bar's grinding. go well 💪
A representative morning brief. The coach reads my Whoop recovery before it programs the day.

I lean on these more than I’d like to admit. The coach has changed how I plan my weeks, and I’d much rather an agent read the overnight commits and hand me the two lines that matter than start the day scrolling a cold git log. That was the idea from the start. They keep working whether or not I’m at a keyboard.

They all share one memory

An agent is only as good as what it remembers, and most setups give each one its own goldfish memory, so every conversation starts from scratch. Mine don’t. The coding agent on my laptop (Claude Code) and the Hermes agent on the server read and write the same memory: one self-hosted temporal knowledge graph that lives on dom.

It’s Graphiti sitting on top of FalkorDB, with a local embedding model, so the notes get embedded on my own box. Only the entity-extraction step ever phones out to an LLM. Both agents reach it over the mesh through an MCP server. Getting the off-the-shelf image to actually work cost me two of those small, maddening detours: one model name that simply 404’d on my provider, and a server that stubbornly bound to localhost when I needed it out on the tailnet. Invisible in the result, an evening each to hunt down. Memories are split into two namespaces, one for the infrastructure and one for the personal stuff, so the two halves of my life don’t bleed into each other.

The effect is faintly uncanny. I’ll fix something on the server from my laptop, and the next time the server’s own agent wakes up, it already knows. A fact one agent picks up, another one recalls days later without being told. It stops feeling like a dozen disconnected scripts and starts feeling like one thing that’s been paying attention the whole time.

I don’t run the server. I ask it.

I almost never SSH into dom to do things anymore. I tell Claude Code what I want, things like “move the photo ML onto the GPU box” or “stand up the memory graph,” and it does the work over SSH. There’s one rule I never break: every change gets written down. An append-only changelog lives in the repo, fifty-odd revisions deep now, each entry recording what changed and why.

That paper trail ended up mattering more than I expected, because it has caught the agent being wrong, including the night it came for the memory itself.

A cleanup step ran a filter that was a touch too greedy and deleted a handful of memory nodes it had no business touching. Nothing dramatic happened on screen. A status check would have stayed reassuringly green. What gave it away was the node count. The next pass noticed the graph had quietly shrunk, flagged it, and restored what was lost. If I’d been trusting a “healthy ✓” I’d never have known. So I made myself a rule:

Don’t trust a status check. Exercise the change end to end. Restart it, confirm the data is still there, and only then call it done.

I call it “200% verification,” half as a joke. Running real infrastructure like this, through an agent, with a log honest enough to indict it, has taught me more about building reliable agent systems than any polished demo could. The whole game is the distance between “it worked once” and “it’s been running, unattended, for months.”

Why I keep doing this

I’m building Company OS, an operating system for running a company, and the homelab is where I get to put my hands on the exact problems I care about there: orchestrating agents, giving them memory that survives a restart, scheduling, and that long, unglamorous stretch between a demo that works and something you’d actually trust to run on its own. It’s the best lab I could ask for. It has to stay up, the data’s mine, and the only person I inconvenience when I break it at one in the morning is me.

If I’m being honest, a lot of this is wildly over-engineered for what it does. A cron job and a few shell scripts would cover half of it. But that was never really the point. I wanted to live inside the kind of system I’m building elsewhere and find out what breaks when you let agents loose on something you care about. The answer turns out to be “plenty of interesting things,” and chasing each one down has been a genuine blast.

So that’s the homelab: I gave it a brain, pointed a few agents at the corners of my life and work where I wanted a bit of leverage, and let them run. These days, mostly, I just read the texts.


Stack: Docker · Tailscale · Hermes (Nous Research) · Claude Code · Graphiti + FalkorDB · Ollama (local embeddings) · Syncthing · Cloudflare Tunnel · Next.js. Three hosts, one shared memory, no open ports.