Homelab as Production/Preface
Preface: Before the Method
Where this project started, and why the tools almost got in the way
About Me
I’m a software engineer of 8 years, recently (~2 years) engineering manager. I spent 7 years at NASA Jet Propulsion Laboratory, and 3 years at Terran Orbital. My background includes Software Engineering, App Development, Model-Based Systems Engineer, some Flight Software for Europa Clipper, and DevOps. At heart I’m someone who loves to learn and grow, and explore new tech on my own time (The wife isn’t always too happy about the 3am coding sessions).
Before the Method & The Wrong Way
Every methodology has a before, and this had a messy before. This series documents 43 sessions of AI-assisted infrastructure work, but it doesn’t start at Session 1. It starts earlier, with a different set of tools, a different way of working, and a crucially fundamental misunderstanding of what AI agents are actually doing.
This post is about that earlier version, and my naive approach to first using AI.
What the Repository Looked Like at the Start
Before the first session in this series, the repository already existed, a modest attempt to build a semi-scalable, production-like homelab. I had five mini-PCs set up with Proxmox and virtual machines, and I was manually deploying Docker containers with Tailscale proxies to get applications on my VPN. Like sysadmins before me, I relied on bash scripts and Ansible for configuration management. It worked well enough. Services ran on my NAS with storage mounted over NFS, but managing each new application or VM by hand was becoming tedious.
I wanted to learn Terraform. Partly because we were starting to adopt it at work, and the best way to speak credibly about a technology with engineers and executives is to actually know it. At the time there were two Proxmox Terraform providers, and it was difficult to map the official Terraform documentation to either of them, so I turned to AI to help bridge the gap.
It also had, if you looked carefully, the artifacts of several AI tools that had been through it before me.
There was a docs/memory-bank/ directory with files like activeContext.md, projectbrief.md, systemPatterns.md. These were generated by a task management AI called TaskMaster. Here’s what one of them said, almost verbatim:
Critical Issues Discovered
- Empty Environment Directories:
production/,staging/,base/completely empty- Minimal Infrastructure: Root Terraform files only contain provider config
- Provider Mismatch: Using
bpg/proxmoxbut standards referencetelmate/proxmox- No State Management: Missing backend configuration for remote state
The irony is that telmate/proxmox was already deprecated at that point. The last registry update was a clear signal, if you knew to look for it. The AI-generated standards were recommending a provider the community had moved away from. The memory files were recording “analysis” that was wrong, and I had no easy way to know that, because I hadn’t written them. They were obscured by the tool’s confident presentation and the assumption that someone smarter had already figured out the details I didn’t know.
There was also a docs/standards/ directory with 11 AI-generated documents: Ansible standards, Kubernetes standards, Terraform standards, security standards, naming conventions, git workflow. The documents were well-formatted and thorough. They were also largely disconnected from the actual state of the repository. They described how things should be organized, not how they were organized. The gap between them and the actual code was significant. I wanted AI to migrate the project to these standards but the process was taking longer than expected per task, each session window hit 120-130k within 1 or 2 prompts.
And there were .clinerules, MCP configuration files, Gemini configuration. Evidence of multiple tools having touched the project, but affecting minimal beneficial change or guidance.
The repository had been through a lot of AI-assisted work. Very little of it had accumulated into anything I fully understood.
The Tool Soup
At the time, my setup looked like this:
VS Code as the editor, with the Cline extension installed. For those who aren’t aware, Cline is an AI coding assistant that takes autonomous actions: it can read files, write files, run commands, call APIs, and chain these operations together without you manually triggering each step. Claude was the underlying model running the intelligence.
TaskMaster as an AI task management layer, a tool for breaking projects into tasks, tracking dependencies, and giving an AI agent something structured to work from.
MCP servers, because everyone was talking about Model Context Protocol. I had several configured. I didn’t fully understand what they were exposing to the AI or how the AI was using them.
What I was doing: describing what I wanted, letting Cline, TaskMaster, and Claude work through it, and receiving output. When things went wrong, I’d try to describe the problem and wait for a fix.
What I thought I was doing: using AI agents to write infrastructure code.
What I was actually doing: pressing buttons and hoping.
Why the Tools Obscured More Than They Revealed
The problem wasn’t the tools individually. Each had legitimate uses. The real problem was the layers of abstraction between me and the AI, which made it impossible to understand what was actually happening.
Token usage was the first and major issue I encountered. Tasks generated by prompt were hitting 100,000+ tokens per session. Sessions were ending preemptively because context windows were exhausted before work was done. I didn’t understand why. Was it the MCP servers flooding context with data? Was Cline reading entire directories into context unnecessarily? Was TaskMaster’s task-tracking format taking up space? I couldn’t tell. Each tool was doing something, and the sum was overwhelming.
Memory files I didn’t write were another signal. TaskMaster and Cline both maintained their own context files: memory banks, active context documents, guidelines. These were supposed to help the AI maintain continuity across sessions. What they actually did was create a parallel set of “facts” about the project that I hadn’t verified and couldn’t easily audit. When activeContext.md said the project had a provider mismatch, was that true? Partially true? Outdated? I didn’t know, because I hadn’t written it.
MCPs I didn’t understand. People in the community were enthusiastic about MCP. “Give your AI access to your Kubernetes cluster! To your PostgreSQL database!” But nobody was explaining what this meant in practice for the quality of output or the token budget. Were the tools actually using the data? Was it helping? I had no visibility.
Guardrails I couldn’t interrogate. Cline creates rule files, TaskMaster creates workflow constraints. These are meant to keep the AI on track. But when you don’t write the rules yourself, you can’t evaluate whether they’re correct. You just see behavior and hope it’s right.
The result: I was getting output, but I couldn’t reason about it. I couldn’t tell good output from plausible-but-wrong output. I couldn’t improve the process because I didn’t understand the process.
The Simplification
The decision to remove Cline wasn’t a single moment. It was a gradual realization that the tools were adding friction and opacity in roughly equal measure to their claimed benefits.
I started with the Claude VS Code extension, a simpler interface, direct conversation, no autonomous agent layer taking actions on its own. The MCP configurations were still there, but they were more intentional and more legible.
The immediate effect: I understood what was happening again. I could see the conversation. I could see what context I was providing. I could evaluate the output against my own understanding of the problem.
The secondary effect: I started actually learning. When Cline had been doing things autonomously, I was observing. When I was writing the prompt, reviewing the output, and deciding whether to apply it, I was participating. The difference was significant.
The Guided Walkthrough
One of the first things that worked well under the simpler setup was a pattern I’d later formalize as the “guided walkthrough.” Instead of Claude generating all the code, it would explain the approach, scaffold the structure, and then have me write the key logic while it reviewed and guided.
This happened naturally during the early Terraform and Proxmox work. Rather than receiving a complete main.tf and accepting it, I was writing pieces of it, getting feedback on what I’d written, and understanding why certain patterns existed. The code that emerged was code I understood, because I’d written meaningful parts of it.
This felt slower at the time. It was also when I started actually retaining things. The gotchas I learned by writing the code, and occasionally getting corrected, stuck. The ones that appeared in output I just applied didn’t.
How Trust Gets Built
The guided walkthrough was the beginning of a trust arc that ran through the rest of the project.
Early sessions: I wrote the code. Claude guided, explained, reviewed, and caught mistakes. The ratio was maybe 70% my keystrokes, 30% Claude’s suggestions.
Middle sessions: Roughly equal. I’d scaffold the structure, Claude would fill in patterns, and I’d review and adjust. We’d argue about architectural decisions. Sometimes I won, sometimes Claude’s rationale was better.
Later sessions: I opened a task, described the scope, and Claude drafted the implementation. My contribution was the PR review, reading what was submitted, evaluating whether it was right, leaving comments, requesting changes. The ratio had inverted: maybe 20% my keystrokes, but 100% my judgment on every merge.
What enabled that inversion wasn’t blind trust. It was a track record. 185 pull requests, each human-reviewed before merging. A growing technical-gotchas.md that documented every failure mode Claude had hit, so future sessions could avoid them. A CLAUDE.md file that captured the conventions of this specific project, not generic best practices, but the actual decisions made in this actual codebase. Custom skills for the workflows we’d built up. Session discipline: bounded scope, PR as the delivery unit, knowledge captured at the end of every session.
Each session added to the context that made the next session better. Not just for the AI, but for me. I understood the system more clearly at session 43 than at session 1 because I’d reviewed every PR in between.
The Arrival at Claude Code CLI
Somewhere along the way, the VS Code extension gave way to the Claude Code CLI, a terminal-first interface that’s become my primary tool for all engineering work. No VS Code required. No extension layer. Just a terminal, the codebase, and a direct conversation.
The CLI gave me tools I hadn’t had before: /start-task to create a git worktree and scope a session, /finalize to do knowledge capture, commit, open a PR, and close out cleanly, and custom skills I could write myself for recurring workflows. The session structure became explicit and systematic rather than improvised.
Context compaction, Claude Code’s ability to compress earlier conversation history when approaching context limits, was genuinely transformative. The sessions that had been hitting 100k+ tokens and ending preemptively could now run longer and more coherently. The AI could maintain context across more of a session without losing earlier work.
The irony isn’t lost on me: the tool I landed on is, in some ways, the simplest interface I’d used. No autonomous agent running in the background. No task management layer. No memory bank I didn’t write. Just a conversation, a codebase, and a clear understanding of what I’m asking for.
Where This Lands on the Skill Curve
Dan Shapiro and Nate B Jones have described a five-level framework for AI coding maturity, modeled after the NHTSA autonomous vehicle levels, that maps almost exactly onto this journey:
| Level | Name | What the human does | What the AI does |
|---|---|---|---|
| 0 | Spicy Autocomplete | All the coding; AI is a search engine | Autocomplete suggestions |
| 1 | Coding Intern | Reviews every line; offloads boilerplate | Writes snippets on request |
| 2 | Junior Developer | Pair programs; still reads all the code | Generates code collaboratively |
| 3 | Developer as Manager | Full-time code reviewer | Writes most of the code |
| 4 | Developer as PM | Writes specifications | Executes the development |
| 5 | Dark Factory | Designs systems and patterns | Autonomously builds, tests, ships |
When I was using Cline and TaskMaster and not understanding what was happening: Level 0-1. The tools were doing things. I was watching.
During the guided walkthrough phase, writing Terraform with Claude explaining each decision: Level 2. Real pair programming, with the ratio shifting gradually.
By session 43, reviewing Claude’s PRs before merging: Level 3. The code review is real work, catching domain-specific mistakes, making architectural judgments, but I’m no longer the primary producer of the code.
The goal this project points toward: Level 4. Writing better specifications, thinking in constraints and outcomes, spending less time in diffs and more time in design. The bottleneck moves from “how fast can I review code” to “how precisely can I describe what should exist.”
That shift, from output reviewer to specification writer, is what the rest of this series is building toward.
What Changed
When people ask what made the difference, I don’t have a single answer. The combination:
Removing abstractions I didn’t understand. When you can see the conversation, you can improve it. When the AI’s actions are legible, you can evaluate them. Opacity is the enemy of learning.
Writing before delegating. The guided walkthrough phase, where I wrote code with AI guidance rather than receiving AI output, built the domain understanding that made me a better reviewer later. You can’t review what you can’t evaluate.
Session discipline as the real infrastructure. The /start-task to implement to PR to /finalize loop isn’t just a workflow. It’s the structure that makes AI-assisted work reviewable, recoverable, and improvable. A commit is a checkpoint. A PR is a quality gate. A knowledge capture is compounded learning.
Trust built through verification, not faith. Every autonomous session Claude ran was followed by a human review before merge. The CLAUDE.md and gotchas registry weren’t trust granted upfront. They were trust earned and documented over time.
Why This Matters for the Series
The rest of this series documents the technical work: Terraform, FluxCD, Keycloak, PostgreSQL HA, Dependency-Track OIDC SSO. The architecture decisions, the debugging arcs, the things that failed.
But the technical work didn’t happen in a vacuum. It happened after I’d learned, through some frustration and a lot of experimentation, how to actually work with an AI agent. Not how to use a tool, but how to collaborate: how to provide context, how to set constraints, how to review output, how to capture what worked and what didn’t.
If you’re starting from where I started, VS Code, an AI extension, enthusiasm, and some confusion about why it wasn’t clicking, that’s the right place to start. The tools will get better. Your understanding of what you’re actually asking for will get better faster.
The method isn’t the tools. The method is the clarity about what you want the AI to do, what you’ll review before applying, and what you’ll do with the failures when they happen.
Next: Post 1 — The Production Homelab Thesis
This series documents a real homelab build. All IP addresses, hostnames, and personal identifiers have been replaced with generic examples. The public reference repository is available at https://github.com/EDKarlsson/homelab-as-production-iac .