Running a Production-Style Homelab with Proxmox, k3s and Terraform

October 31, 2026
Kubernetes
Proxmox
Terraform
Ansible
Homelab

My homelab, naboo.party, is where I run infrastructure the same way I would at work: virtualization, Kubernetes, storage and networking, all managed as code. The rule is simple. If a node dies, I should be able to rebuild it from the repo. All of it is public in homelab-infra.

The Layers

  • Virtualization: Proxmox VE hosts the k3s nodes and the supporting VMs
  • Kubernetes: multi-node k3s with Traefik as the ingress controller and reverse proxy
  • Storage: TrueNAS with ZFS pools, serving NFS shares to cluster workloads
  • Database: a dedicated PostgreSQL instance outside the cluster, with nightly dumps to TrueNAS
  • Networking: UniFi for firewall, routing, DHCP, static IP assignment, switching and wireless

Provisioning with Terraform

Terraform creates the VMs on Proxmox for the k3s clusters, PostgreSQL and monitoring. There are separate dev and prod clusters, so I can break things in dev before they reach anything I rely on.

Configuring Nodes with Ansible

Once the VMs exist, Ansible installs k3s and puts kube-vip in front of the API server so the clusters have a stable virtual IP. PostgreSQL and the monitoring stack get their own playbooks. Between Terraform and Ansible, every node can be rebuilt from scratch.

GitOps with Flux

Terraform and Ansible stop at the cluster boundary. Everything inside the clusters comes from Git through Flux, which applies controllers first, then configuration, then apps, waiting for each step to be healthy. Nothing is applied by hand, so a change is a commit.

Image updates work the same way. CI builds the image, Flux scans the registry for new tags, and its image automation commits the new tag back to the repo. Dev takes the newest build and prod takes the newest release tag.

TLS and Secrets

cert-manager issues a wildcard Let's Encrypt certificate using DNS-01 challenges. No secrets are committed: they live in 1Password, and External Secrets reads them into the clusters, while Terraform and Ansible fetch them at run time.

Observability

A dedicated monitoring VM runs Prometheus, Grafana and Loki, with email alerts. Keeping it outside the clusters means I still get alerted when a cluster is the thing that's broken.

Conclusion

A homelab doesn't need to be this involved, but running it like production is the point. It's where I try the patterns I use on the job, and it keeps me honest about whether a system can really be rebuilt from code.