From Docker Compose to Infrastructure as Code with OpenTofu
How I went from SSHing into servers and running docker-compose up to managing the whole homelab with OpenTofu, Infisical and a deploy pipeline.

For the first two years of my homelab, deployment meant SSH into a server, git pull, and docker-compose up. It worked. Until it didn't. The day I accidentally overwrote an .env file and took down three services at once, I decided it was time to grow up.
The problem with manual deployments
When you manage a handful of containers, clicking around is fine. When you manage 14 Docker Compose stacks across 5 VMs with secrets, reverse proxy configs, and interdependencies, 'just SSH and fix it' stops being a strategy and starts being a liability. I had no audit trail, no rollback path, and no way to reproduce my setup if a drive died.
Why OpenTofu
I chose OpenTofu (the open-source Terraform fork) because it has providers for everything I need: Proxmox VMs, Docker containers, Cloudflare DNS, and file templating. The HCL syntax is readable enough that it doubles as documentation. And unlike Ansible, it maintains state, so it knows what exists and what needs to change.
The pipeline
Every change goes through Git. When I push to a branch and open a PR, a webhook triggers a plan. The diff shows up as a PR comment so I can review exactly what will change. When I comment /apply, it runs. No SSH, no manual steps, no surprises.
Secrets management
The hardest part wasn't the IaC itself. It was secrets. API keys, database passwords, tokens. I self-host Infisical for this. The deployment pipeline authenticates with a machine identity, fetches secrets at deploy time, and injects them into Docker Compose .env files. Nothing sensitive lives in the Git repo.
Was it worth it?
Absolutely. Last month a drive failed and I had to rebuild a VM from scratch. What used to be a weekend of archaeology through shell history and half-remembered configs was a single pipeline run. The infrastructure rebuilt itself in 20 minutes. That alone justified every hour I spent writing HCL.