My dev box was running production
Counting containers is what tipped me off. I’ve deliberately invested in powerful hardware on the production side of my homelab, relative to a Mac Mini that is supposed to be a lightweight development box. And yet some of my most critical agents ran on the Mini while depending on the homelab’s GPU, adding a second machine that had to stay available. Mailroom, the morning brief, and the jobs behind Coach and Scout all lived on the Mac. In trying to keep development and production apart, I had introduced fragility and cross-host dependencies where none were needed.
The separation itself was deliberate. At the time, the Mini ran the frontier labs’ command-line tools natively, and those tools had read-only access to the homelab: enough to monitor for issues, confirm a deployment landed, and reason about what to optimize next. They couldn’t change what ran there; changes went through a deploy block I pasted myself in Termius, my go-to terminal app. What I couldn’t explain was how that boundary had turned into a reason to keep the services themselves on the Mac. If the homelab being down makes these jobs dead anyway, why isn’t the homelab the default?
I put that question to my own tooling, which had reviewed the placement of every one of those jobs the day before and concluded that nothing should move, and asked what I was missing.
The answer was nothing. The reasons on file did not survive being read back. One was a note about a watcher, a job whose whole purpose is to sit outside the thing it observes, stretched into a rule for every job. Another said the winning host is whichever one deploys with the least friction, which describes a workflow rather than a principle. The third said a handful of jobs read the Mac’s filesystem, and an audit mostly dissolved it: a path described as hardcoded was a configuration key, and one job had no coupling to the Mac at all. My harness and I had agreed in principle on the shape my deployments should take. It took my coaxing to check whether that shape was real, and I had no principled stance to defend how it was actually implemented.
What surprised me was how much my AI tooling fought me on the migration. It was blatantly wrong multiple times, and it kept defending the placement after I showed it evidence to the contrary. It wasn’t hard to disprove, but that it fought at all did upset my faith and trust in the machinery a bit. Ninety minutes after the first round it made the same move again, ruling a job unmovable because it spawns a command-line tool, with the disproving evidence in its own decision log from July: that tool already authenticates headlessly, with nothing from the Mac’s keychain. The corrected test: driving a macOS-only binary or a logged-in desktop app ties a job to the Mac; spawning a tool that runs anywhere is a line in a Dockerfile.
What stayed on the Mac now stays for a reason instead of inertia. The watchers that check the health of other containers, and the bot that carries my alerts, sit on the Mac, because my fire marshal doesn’t live inside the fire station. A job that drives a logged-in browser stays for the same reason the test names. One job is a macOS-only binary that genuinely cannot move, which I’d rather log as debt than dress up as a principle. Most of the rest now runs in its own container on the Linux homelab, and the jobs that lean on the GPU now sit next to it.