After moving away from Puppet, I wanted a system that was simpler to manage, easier to extend, and aligned with how modern infrastructure is being handled — especially in smaller environments like a homelab.

The result is a new, GitOps-inspired setup that combines:

  • Terraform (OpenTofu) for provisioning VMs on Proxmox
  • Ansible for configuration management
  • Gitea for hosting code and triggering automation via CI/CD workflows
  • Gitea Runners, both bare metal and in Kubernetes, to apply configuration automatically
  • k3s as my lightweight Kubernetes cluster for services and further automation
  • Garage as an S3-compatible backend for storing Terraform state

The core idea is this: everything lives in Git. Infrastructure, configuration, secrets (where appropriate), and automation. Whenever I want to change something — provision a new VM, update a service, or tweak a config — I commit the change, push it to Gitea, and let the automation take care of the rest.

It’s lightweight enough for a homelab, but structured enough to be repeatable, maintainable, and close to how production infrastructure is managed in modern DevOps environments.

Here is a little brainstorm session I did to organize all of this: diagram

Why?

When I first set up my homelab, I chose Puppet as my configuration management tool. My goal was to better understand how it works under the hood — not just from the perspective of day-to-day maintenance, but from the ground up.

In my day job, I manage Linux infrastructure at scale, and Puppet plays a central role there. It’s an excellent fit for our environment: we have a large, multi-environment landscape where maintaining state is critical for consistency, scalability, and reliability. Puppet excels at enforcing desired state, which keeps things stable and predictable.

Using Puppet at home helped me think more deeply about how everything ties together: the node communication model, the lifecycle of configurations, and the role of a centralized control server. That experience gave me a much stronger grasp of the full lifecycle — far beyond just running puppet agent -t.

But now that I’m more familiar with it, I’ve realized that Puppet’s complexity and always-on enforcement model isn’t necessary for a small homelab. I don’t need the same level of rigor or centralized state management — I just need something that can declaratively configure machines, is easy to maintain, and aligns better with modern DevOps tooling.

That’s why I’ve decided to transition to Ansible. It’s lighter, more flexible, and arguably more future-proof. It’s also far more common in the industry today, so getting more hands-on experience with it will be valuable for me professionally.

Of course, there are trade-offs. Ansible is agentless, which means there’s no persistent daemon to enforce state. I’ll need to ensure my roles and playbooks are applied idempotently and regularly — especially to avoid configuration drift when I make manual changes and forget to codify them. But with the right GitOps-style automation, I believe it’ll be a clean and efficient setup going forward.

Let’s get technical in the next part.