This is Part 7 of the Building a Homelab with AI series. Previously: Security Review | Next: Sudoers Demystified
The Experiment
This entire project — from empty repo to fully codified homelab infrastructure — was done in conversation with Claude Code, Anthropic’s AI CLI tool running Claude Opus 4.6. Every playbook, every compose file, every Caddyfile template, every security review. All of it was a back-and-forth between me and an AI pair programmer, working directly in the terminal.
Every AI-assisted commit includes Co-Authored-By: Claude Opus 4.6 <[email protected]> in the message. Not to give credit to a machine, but to be transparent about how the work was done.
Here’s what I learned.
What Worked Well
Rapid iteration on unfamiliar territory. I’ve been a Linux admin for 26 years, but I don’t write Ansible playbooks every day. Having Claude generate a first draft, explain the module options, and immediately course-correct when something failed was like having a senior Ansible engineer on call. The feedback loop was minutes, not hours of documentation reading.
Real-time debugging. When an Ansible playbook failed, I could paste the error output and get an analysis and fix in the same conversation. The bootstrap saga — three failed approaches before landing on pct exec — would have taken me a full evening alone. With Claude, each attempt was a five-minute cycle.
Security review with fresh eyes. Asking Claude to review the entire repo for security issues surfaced things I’d walked right past. The .vault_pass gitignore gap and the Open WebUI 0.0.0.0 binding were both real findings I would have missed because I was too close to the code.
Documentation generation. You’re reading it. Claude drafted blog posts, README content, and architecture documentation while the technical details were still fresh. Infrastructure documentation is the thing everyone knows they should write and nobody does. Having an AI that can generate a first draft from the conversation context removes the biggest barrier.
What Required Course Correction
The bootstrap playbook was the hardest part. Claude’s first instinct was to use -u root on the command line. Reasonable, but wrong — Ansible’s variable precedence means inventory-level ansible_user overrides CLI -u. Then we tried -e ansible_user=root (extra vars have highest precedence), which connected fine but hit the sudo-not-installed wall. Then become: false with raw commands. Three approaches, each logically sound, each failing for a different reason.
The solution — using pct exec through the Proxmox host — wasn’t something Claude suggested first. It came from me thinking about what access I actually had. Claude then wrote the playbook quickly once the approach was clear. This is the right dynamic: human sets direction, AI executes. The AI is great at “write me a playbook that does X” but less reliable at “figure out the right approach when the obvious one doesn’t work.”
Ansible variable precedence is genuinely confusing. The group_vars directory placement issue — it has to be adjacent to the inventory file, not at the Ansible project root — is another example. Claude initially placed it at ansible/group_vars/, which is wrong when the inventory is at ansible/inventory/hosts.yml. We caught it fast, but it’s the kind of subtle structural issue where you need to actually run the code to discover the problem.
Broader Lessons
Incremental commits are non-negotiable. With an AI generating code rapidly, the temptation is to make huge changes and commit once. Don’t. Small, atomic commits with descriptive messages saved us multiple times when we needed to understand what changed and why. The Git log tells the story of the project.
Consistency beats optimization. I chose Ubuntu over Alpine for the DMZ Caddy VM. I chose systemd over a Docker container for a single Go binary. These aren’t the most minimal choices, but they’re consistent with everything else in the homelab. When you’re the only operator, reducing cognitive load matters more than saving 50MB of disk space.
Always have a rollback plan. Before destroying the old Nginx Proxy Manager LXC, I kept it stopped for a week. Before cutting DNS over to the new Caddy VM, I tested with direct IP requests. Before deleting the Docker Caddy stack, I verified the new one was handling traffic. This discipline is easy to skip when an AI is helping you move fast. Don’t skip it.
The AI doesn’t replace judgment. Claude is excellent at generating configurations, explaining options, catching mistakes, and iterating quickly. But the architectural decisions — DMZ isolation, VM over LXC, no Docker on the proxy — those came from 26 years of knowing what matters when something breaks at 3 AM. The AI accelerates execution; the human provides direction and taste.
What’s Next
The homelab IaC foundation is solid. The backlog includes:
- Monitoring and alerting: Prometheus, Grafana, and Loki for observability across all hosts
- Dedicated DNS server: Moving off the edge firewall’s built-in DNS to a proper LXC running something like AdGuard Home or Unbound
- Tighter DMZ firewall rules: Currently the DMZ can reach LAN backends on any port. Should be locked down to only the specific backend ports Caddy needs
- Backup automation: Ansible playbooks for Proxmox snapshot management and off-site backup verification
Each of those is another conversation with Claude Code, another set of commits, another blog post. The homelab is never done — and now every change is documented, version-controlled, and reproducible.
That’s the real win. Not the AI. Not the tooling. The discipline of treating your homelab like it matters. Because it does.
Up next, we pivot to a deep dive on Linux security fundamentals: Sudoers Demystified.