The HomeLab Chronicles

Omega: Building a Persistent Claude Code Environment in the Homelab

The Problem With Laptop-Based AI Workflows

For the past several months, my AI-assisted development workflow has lived entirely on my MacBook Pro. Claude Code sessions open and close with my laptop. Context resets. History disappears. If I'm away from the machine, I'm away from the workflow.

That works fine for casual use. It stops working when the workflow becomes infrastructure — when you need persistent context, access to running services, and a Claude Code environment that doesn't depend on you being in front of it.

The answer was obvious: add a dedicated machine to the homelab.


Enter Omega

I picked up a Mac mini M4 (16GB unified memory) and named it Omega. Fits the Doctor Who naming convention I've been running — Gallifrey, Citadel, Arcadia, and now Omega. It runs headless, SSH only, and sits on the same local network as the rest of the lab.

The goal wasn't to run a GPU cluster or replace my laptop as a daily driver. The goal was a persistent, always-on Claude Code environment that knows the homelab and can work in it — whether I'm at my desk or not.


The Stack

I kept the install footprint intentional. Everything installs under the admin account (omega) via Homebrew, and the working account (logos) inherits what it needs through PATH configuration.

Base tools: Homebrew, git, tmux, curl, wget, jq. Node.js v25.8.2 for the MCP server work. Nothing exotic.

Claude Code v2.1.87 — authenticated via OAuth, pulling its config from gitlab.com/cybersecuritybro/claude-config. My Claude Code configuration is version-controlled and synced across machines. No manual setup on a fresh machine beyond auth.

Doppler CLI v3.75.3 — the logos account has a service token scoped to its own Doppler project. Git uses doppler run to inject GITLAB_TOKEN at runtime. No credentials in the shell profile. No keychain entries. No plaintext anything.

# .zprofile — the relevant bits
eval "$(/opt/homebrew/bin/brew shellenv)"
export DOPPLER_TOKEN="<service-token>"
export PATH="$HOME/.local/bin:$PATH"

Workspace: ~/Documents/gitlab-repos/ — currently holding herreralab-blog and homelab-mcp. More repos will land here as the workflow expands.


Where Local Small Models Fit

Not every task needs a frontier model. That's the honest truth of working with AI tooling at this scale.

I run Ollama as a brew service on Omega with qwen2.5-coder:7b loaded — 4.7GB model, CPU-only inference on Apple M4. Not blazing fast, but it doesn't need to be. The use case isn't speed, it's cost and appropriateness.

The mental model I've settled on:

  • Local model (Ollama): code review passes on known patterns, documentation first drafts, low-stakes reasoning, anything where I want a sanity check without burning API credits
  • Claude Code (Sonnet/Opus): architecture decisions, novel problems, anything touching security, anything where the answer actually matters

The M4's unified memory architecture makes CPU inference more viable than it sounds. qwen2.5-coder:7b handles TypeScript and shell scripting well. It's not a replacement — it's a filter. The tasks that don't need Opus shouldn't go to Opus.

Having both available on the same persistent machine means I can wire them together properly — route by task type, not by what's convenient in the moment.


The homelab-mcp Project

One of the first repos cloned onto Omega was homelab-mcp.

This is a custom Model Context Protocol server I've been building in TypeScript. The idea: expose Kubernetes cluster operations as callable tools that Claude Code can invoke directly. Not "here's context about my cluster," but "here's a function you can call to check pod health, query ArgoCD sync status, or pull recent cluster events."

The architecture runs on stdio transport for maximum client compatibility. It deploys to k8scontrol via ArgoCD. Cluster access is tiered:

  • k8scontrol: in-cluster ServiceAccount when deployed
  • gallifrey-prod + t8sdev: dedicated kubeconfigs pulled from Doppler via ESO

The L1 read-only tools are done. L2 — soft actions like pod restarts and ArgoCD syncs — is in progress.

Building it exposed something worth noting: several of the tools I'm building already exist as vendor MCPs. Wiz ships an official MCP. ArgoCD has one via akuity/argocd-mcp. The smarter move is to wire those in for what already exists, and keep homelab-mcp for the glue — the custom aggregate tooling that vendors don't cover.


What This Is Building Toward

Omega isn't interesting because it runs Ollama or hosts Claude Code. It's interesting because of what becomes possible when those things are persistent and connected.

Right now, every AI-assisted workflow I run is reactive. I notice something, I open Claude Code, I ask a question, I get an answer. The AI is a tool I pick up and put down.

The direction I'm building toward is different. Claude Code — with MCP tools wired in — that can query cluster health, surface ArgoCD drift, pull Wiz security posture on a workload, and flag what's changed. Local inference handles the lightweight reasoning. The API handles the hard problems. The MCP layer is what gives Claude Code actual hands in the environment.

Not a chatbot that knows my cluster. An environment where Claude Code can operate in it.

Omega is the execution layer for that. The MCP servers — both custom and vendor — are the action surface. The Claude config synced from GitLab is the knowledge layer that travels with the environment.

It's early. The pieces are just starting to come together. But the foundation is right.


What's Next

The immediate work:

  • Complete L2 tools in homelab-mcp — pod restarts, ArgoCD sync triggers
  • Add the ArgoCD MCP alongside homelab-mcp for the full action surface
  • Security review: run Wiz against the MCP deployment itself — what does Claude Code with cluster access look like from Wiz's own scanner?

That last one is going to be its own post.