John Kozaris
Blog

Made myself addicted to improving my homelab

It started with an ad blocker. Then AI made every upgrade one sentence long, and I lost the way I used to lose at Age of Empires.

7 min readHomelabAgents

On Christmas Day 2025 I told my friends I had finished my home server. I posted a picture in our group chat with the caption “Final home server.”

By February it ran 23 services, and some of them I never used once. In May it froze at seven seconds past ten in the morning. The most likely culprit was a scraper I had installed for my AI agents, and they never needed it.

An ad blocker for the whole flat

It began with ads. After Chrome switched off my ad blocker, the web got noisy. I wanted the ads gone everywhere, on the phones and on the TV too.

The way to do that is DNS. Before a device loads anything, it asks a DNS server to turn a name like example.com into an address. Run that server yourself, and you can refuse to answer for the names that serve ads. So I put AdGuard Home on a mini PC and made it the DNS server for every device in the flat.

The box drew about 5 W doing this. A computer that cheap to leave running makes you wonder what else it could run, and that question was the start of my problem.

Improving it became the hobby

Every new service was a small project with a satisfying end. I’d find an app in a list of self-hosted software and give it its own name under my domain. Then it went behind the proxy, got a tile on the dashboard, and I went looking for the next one.

By 5 January, when I finally put the setup in a Git repository, the box was running 19 services. Among them were a wiki, a whiteboard, a read-later app, a QR code generator and a vault for passwords.

I wrote little of it by hand. I’d describe what I wanted, and Claude Code wrote the Ansible playbook that installed the service and the docs that explained it. Without an agent, a new service would have cost me an evening of reading. With one it cost a sentence, and the agent never once asked whether I needed a third dashboard.

I shared it with my friends as well. Three of the services went on the open internet so they could use them, and an agent emailed each friend a username and password. It did that perfectly, and nobody should ever do it.

The same hit

I’ve been like this since I was a kid. Like a lot of millennials I grew up on Red Alert and Age of Empires, and I played them wrong. I researched every weapon and every technology before I built an army. I loved upgrading for the act of upgrading, and I still don’t know what to call that.

In a strategy game it’s a sure way to lose. The other side attacks early, and you meet them with the best blacksmith on the map and no army at all. Programmers have a name for the grown-up version, premature optimization. Donald Knuth called it the root of all evil.

Homelabbing gives me the same hit. Every service is another upgrade, and there is always one more on the list. AI turned the hit up, because the upgrades stopped costing anything but a sentence.

The night it tipped over

My AI psychosis moment came on 11 February, at 1:32 in the morning. The box ran 23 services by then, and I decided my agents should run them for me. So I started a new project, an MCP server and a dashboard that would let any agent read and manage the whole homelab.

By lunchtime it had a README, a license and CI, ready to go open source. By four in the afternoon it had tests and two complete redesigns, and the second one gave it a landing page. Who builds a landing page for a tool whose only users are their own agents? I did, and a month later I asked my friends to star it on GitHub.

I use the word psychosis loosely, and carefully, because the real thing is serious. Psychiatrists now study patients whose chatbot kept agreeing with them until they lost their hold on reality. In February 2026 OpenAI withdrew a GPT-4o model that had a habit of flattering its users.

I never lost touch with reality. But for a few months neither the agent nor the box ever told me no, and I kept asking for more.

Agents moved in next

In January an open-source agent called Clawdbot went viral, and the internet filled with jokes about buying a Mac mini to run it. Anthropic objected to the name, so within days it became Moltbot, and then OpenClaw.

Of course I gave it a home. OpenClaw got its own container on the server, and Hermes Agent got the one next to it. I named them mario-claw and luigi-hermes. The dashboard grew an Agents tab and the monitoring grew 13 alerts. In April I set up a new server, and I installed a web scraper called Firecrawl so my agents could read web pages.

Ten o’clock

On 14 May the server went dark. Every device in the flat asked it for addresses, so the internet went with it, and I still don’t know what killed it that day. At 1:38 the next morning I deleted eight services in one go. Out went the wiki, the whiteboard, the RSS reader, the read-later app, the QR code generator and three more. The commit says it freed about 600 MB, which tells you how little those eight ever held.

On 17 May it froze again. Its log stops at 10:00:07 with no error at all, as if someone had pulled the plug. It still had 21 of its 28 GB of memory free. Six seconds earlier, a timer had started all five of Firecrawl’s containers at once, the way it did every morning at ten.

Nobody ever proved that Firecrawl killed it, and the best guess is a network stack choking on a burst of new virtual interfaces. It was the Age of Empires kid all over again. I had a server full of upgrades, and when the attack came, the one thing the whole flat needed went down with everything else. That afternoon I deleted Firecrawl too, because Claude Code could already read the web.

5 January19 services
11 February23 services
3 April22 services
15 May12 services
17 May11 services
Today16 services
Services defined in the homelab's repository at each point. The peak came in February.

What I never used

Some upgrades never saw a battle. These are the services I set up and never used:

  • Portainer, a web interface for managing Docker. When I deleted it, it was managing zero containers.
  • Uptime Kuma, a status page for my own services.
  • Stirling PDF, a toolbox for merging and splitting PDFs, and IT-Tools, a page of small developer utilities.
  • The QR code generator, the wiki, the whiteboard, the RSS reader and the read-later app.
  • Firecrawl, the scraper for agents that could already read the web.
  • The MCP server and its landing page, which I moved out of the homelab in April.

Far fewer stayed. The ad blocker, Home Assistant, Paperless for scanned mail, Nextcloud for files and Forgejo for my private Git repositories are still there. The photo library came back after I deleted it, which is how I found out I used it.

If you want one

If you want to build your own, start where I started and stop much sooner. Blocking ads with DNS pays off on the first day, and AdGuard Home or Pi-hole on any small, low-power box is enough. Give it two public resolvers for its own lookups, so it never has to ask itself how to reach the internet.

# AdGuardHome.yaml, under dns:
bootstrap_dns: [1.1.1.1, 9.9.9.9]
fallback_dns: [1.1.1.1, 9.9.9.9]

Point Docker at it too, with public resolvers behind it, so containers can still pull images while AdGuard restarts. This goes in /etc/docker/daemon.json, with your AdGuard box first. The live-restore setting keeps containers running while the Docker daemon itself restarts.

{
  "dns": ["192.168.1.50", "1.1.1.1", "9.9.9.9"],
  "live-restore": true
}

Don’t open ports. I ran a Cloudflare tunnel and put three services on the internet for my friends, and in May I took all of it down. Now the server answers only on my Tailscale network, and I let SSH in only there.

sudo ufw allow in on tailscale0 to any port 22 proto tcp

Keep the whole setup in a Git repository, as Ansible playbooks or plain compose files, and pin every image to an exact version. When the server dies, you rebuild it from the repository instead of from memory. Before any change that deletes something, take a backup and restore it once to prove it works.

Let the box reboot itself instead of hanging. These kernel settings turn a panic, an oops or a stuck CPU into a reboot after ten seconds.

# /etc/sysctl.d/90-recover.conf
kernel.panic = 10
kernel.panic_on_oops = 1
kernel.softlockup_panic = 1

Be careful with timers. My worst outage lined up with a job that brought up five containers at once, every morning at ten. Start heavy services once and leave them running, or delete them.

If an agent does the typing, put your rules in the repository, where it reads them first. Mine make it take a snapshot and test a restore before anything destructive. They also keep it to Ansible for any network change, and to images pinned by digest. And now and then, ask it which services you could delete. It answers that question as happily as every other one.

Sixteen services later

The box runs 16 services now, seven fewer than in February. October still brought more changes than any month before it, and many of them were updates and a living room PC that learned to sleep. So the habit hasn’t gone anywhere, and I still love an upgrade.

The difference is the order. These days I build the army first. That means backups that get a test restore, a written way back when DNS breaks and a box that reboots itself. Only then do I go back to the blacksmith.

Read next