Architecture
Every layer of the homelab is described in git and applied by a pipeline. A push is the only way changes reach the machines.
The layers
| Layer | Tool | Repo |
|---|---|---|
| Hypervisor | Proxmox VE, 3 nodes | — |
| Provisioning | OpenTofu (bpg/proxmox) |
homelab_terraform |
| Configuration | Ansible | homelab_ansible |
| Cluster | k3s + kube-vip | homelab_k3s |
| Workloads | ArgoCD + Kustomize | homelab_kube-apps |
| Dependencies | Renovate | renovate-config |
Source control and CI run on Gitea with Gitea Actions. Runners live both on a VM and inside the k3s cluster.
Adding a VM
- Add an entry to a
.tfvarsfile: size, Proxmox node, template and tags. - Push. The workflow runs
tofu planandtofu applyin the matching workspace. State is stored in a self-hosted Garage S3 bucket. - OpenTofu also creates a DNS rewrite in AdGuard Home for the new VM.
- For new VMs only, the pipeline waits for SSH and runs the Ansible site playbook against them.
Proxmox tags become Ansible groups through the dynamic inventory, so a tag decides which roles a VM gets. Hosts that were never created by OpenTofu sit in a small static inventory next to it.
Configuration
Each Ansible role has its own Gitea Actions workflow, triggered by changes to the role or its group variables. A daily run applies the base roles (common, node_exporter, alloy) to the whole fleet to catch drift.
The cluster
k3s runs on nine VMs spread over three Proxmox nodes:
- 3 control plane nodes behind a kube-vip virtual IP
- 3 worker nodes
- 3 storage nodes running Longhorn
The bundled servicelb and Traefik are disabled. kube-vip handles LoadBalancer services, and Traefik is installed through ArgoCD with my own values. See GitOps for how workloads get deployed.
Outside the cluster
Some services run on dedicated VMs, configured by Ansible:
- Gitea + Jenkins: source control and CI
- OpenBao: 3-node secrets cluster, see Secrets
- Garage: 3-node S3 storage for OpenTofu state and database backups
- Prometheus, Grafana, Loki, Alertmanager: see Monitoring
- Media server and Home Assistant
A Synology NAS stores the backups. A Raspberry Pi acts as bastion host and VPN entry point.