For about a year my homelab ran on a single docker-compose.yml file that kept growing until nobody, least of all me, wanted to touch it. Every new service meant another fifteen lines pasted from the last one, and every change meant hoping I remembered which container depended on which network.
The breaking point was a Pi-hole restart that quietly took down the reverse proxy with it, because they shared a network alias I’d forgotten I’d set six months earlier. That’s the kind of bug that Terraform’s dependency graph is specifically built to catch before it happens, not after.
I moved the Proxmox layer to Terraform using the bpg/proxmox provider, and kept Ansible for anything that touches secrets or does in-place configuration. The split sounds obvious written down, but it took a few false starts to land on:
The moment you let one resource exist outside the state file, the state file stops being the truth.
That last rule is the one I break the most, and it’s the one that costs the most time later.
Here’s a trimmed version of the module that provisions the GitHub Actions runner. Nothing clever, just pinned versions and explicit resources instead of hand-edited yaml:
# runner.tf
resource "proxmox_vm_qemu" "runner" {
name = "gh-runner-01"
target_node = "pve"
cores = 2
memory = 4096
disk {
size = "32G"
storage = "local-lvm"
}
}Six months in, the thing I notice most isn’t speed, it’s confidence. I can tear the whole runner down and rebuild it from nothing in under a minute, which means I stopped being precious about it, which means I actually experiment with it now.
I’d have pulled Terragrunt in from day one instead of month three. Wrangling multiple environments in raw Terraform works fine until it very suddenly doesn’t.