For my new Role as DevOps Engineer, i was spending hours on video training ( Udemy, LinkedIn, YT .. ) . Even thought there is good material out there – and i want to explicit name Nana’s Tech World – it is sometime cumbersome, slow paced, not on the spot. So having own ideas how and what to learn, i got myself an Instructor aka #Claude who will not only assist me but answer all my upcoming questions during the process.
For this project: I wanted a repeatable Kubernetes foundation on Proxmox where the infrastructure definition lives in Git, the deployment process is automated, and the important decisions are documented. The result is a small GitOps-style homelab project built with Proxmox, Terraform, Ansible, Kubespray, Forgejo Actions, and an S3-compatible Terraform state backend.
This article is not a full installation guide. The detailed implementation lives in my GitHub MRi-LE/proxmox-kubernetes-gitops: A GitOps-style Kubernetes homelab on Proxmox using Terraform, Kubespray, Forgejo Actions and S3-backed Terraform state. repository.
Instead, this post explains the concept, the architecture and the overall approach.
The Goal
My goal of the project was to build a Kubernetes homelab that can be created, reviewed, destroyed, rebuilt, and improved/expanded over time. The focus was not to create a full production platform but to create & document a clean and understandable foundation to play with and learn during the process.
The core idea is simple:
Git stores the desired setup.
Forgejo runs the workflows.
Terraform creates the virtual machines.
Ansible and Kubespray install Kubernetes.
RustFS stores the Terraform state.
Proxmox runs the cluster.
This turns the Kubernetes cluster from a one-time manual installation into something closer to infrastructure-as-code.
The Stack
The project uses the following components:
| Layer | Tool | Purpose |
|---|---|---|
| Virtualization | Proxmox | Runs the Kubernetes virtual machines |
| Provisioning | Terraform | Creates the Ubuntu template and cluster VMs |
| Kubernetes installation | Ansible + Kubespray | Installs and configures Kubernetes |
| CI/CD | Forgejo Actions | Runs Terraform and Ansible workflows |
| State backend | RustFS / S3 | Stores Terraform state outside the runner |
| Repository | GitHub / Forgejo | Stores code, workflows, documentation and tests |
The important design decision is the separation of responsibilities.
- Terraform does not install Kubernetes. It creates the infrastructure underneath it.
- Kubespray does not create Proxmox virtual machines. It installs Kubernetes once those machines exist.
- Forgejo Actions does not contain the infrastructure logic. It orchestrates the workflows that call Terraform and Ansible.
That separation makes the setup easier to understand, troubleshoot, and extend.
High-Level Architecture
The overall flow looks like this:
+-----------------------------+
| GitHub / Forgejo Repository |
+--------------+--------------+
|
v
+-----------------------------+
| Forgejo Actions |
| proxmox-infra runner |
+--------------+--------------+
|
+-------+--------+
| |
v v
+-------------+ +-------------------+
| Terraform | | Ansible |
| Proxmox IaC | | Kubespray |
+------+------+ +---------+---------+
| |
v v
+-------------+ +-------------------+
| Proxmox | | Kubernetes |
| VMs | | Cluster |
+-------------+ +-------------------+
Terraform State:
+-----------------------------+
| RustFS / S3 on TrueNAS |
+-----------------------------+
In my setup, the cluster consists of one control plane node and two worker nodes. The virtual machines are based on Ubuntu 24.04 cloud-init images and are created on Proxmox through Terraform.
Kubernetes itself is installed with Kubespray. The Kubespray version is pinned so that the deployment remains predictable.
Why Terraform?
Terraform is responsible for the Proxmox layer.
It creates the VM template and then clones that template into the Kubernetes nodes. It also controls the VM resources, static IP configuration, cloud-init settings, disks, and related Proxmox configuration.
Without Terraform, these steps would usually be performed manually in the Proxmox web UI. That works once, but it is difficult to reproduce exactly later.
Also with Terraform, the VM layer becomes declarative:
This is the template.
These are the nodes.
These are the IPs.
These are the disks.
This is the expected state.
That is the part I wanted in Git.
Why Ansible and Kubespray?
Once the virtual machines exist, the next problem is installing Kubernetes.
Kubernetes installation involves many moving parts: certificates, kubeadm, container runtime, CNI, DNS, control plane components, worker joins, and configuration files. Kubespray already solves this using Ansible.
So the split is:
Terraform = create the machines
Kubespray = install Kubernetes on the machines
The project also includes pre- and post-install Ansible playbooks around Kubespray. These prepare the nodes before installation and collect the kubeconfig afterwards.
Why Forgejo Actions?
The workflows are executed with Forgejo Actions, using my self-hosted Forgejo instance as the Git forge of choice. The same general approach could also be adapted to other CI/CD systems such as GitHub Actions, GitLab CI/CD, Woodpecker CI, Drone, Jenkins, or similar pipeline-based automation tools.
For a homelab project, I did not want every commit to automatically create or destroy infrastructure. That would be too risky. Instead, the destructive or expensive actions are triggered manually.
The general flow is:
Pull request
|
v
Terraform plan
|
v
Review changes
|
v
Manual Terraform apply
|
v
Manual Ansible/Kubespray deploy
|
v
Download kubeconfig
|
v
kubectl access
This keeps the process controlled.
Planning can happen automatically. Applying infrastructure changes stays deliberate.
Remote Terraform State
Terraform state is stored in an S3-compatible backend provided by RustFS on TrueNAS.
RustFS is my current object storage backend of choice because it was already available in my homelab and easy to set up. The setup is not tied to RustFS specifically; any S3-compatible object storage backend should work as long as Terraform can use it for remote state..
A local terraform.tfstate file would tie the infrastructure state to one machine. That is not ideal when the work is executed by a CI runner.
With remote state, the runner can be recreated and the state still exists outside of it.
In this project, RustFS provides the S3-compatible object storage, and Terraform uses that backend for the homelab environment state.
Repository Structure
The repository is structured as a small monorepo:
proxmox-kubernetes-gitops/
├── .forgejo/workflows/ # Forgejo Actions workflows
├── terraform/ # Proxmox VM provisioning
├── ansible/ # Ansible playbooks and Kubespray integration
├── tests/ # Python tests for inventory generation
└── docs/ # Lab book, runbooks and troubleshooting notes
The docs/ directory is more than a simple collection of notes; it is organized as a sequential lab book.
The guided chapters cover the journey from concepts and environment preparation to Proxmox setup, RustFS state backend, Forgejo runner setup, Terraform VM provisioning, Kubespray deployment, and kubectl access.
The later chapters cover operations, troubleshooting, security/key rotation, upgrades, and next steps.
That structure is intentional. I wanted the repository to explain the project, not just contain the files.
What This Project Is
This project is:
- a reproducible Kubernetes homelab foundation
- a Proxmox-based infrastructure-as-code project
- a practical Terraform and Ansible learning setup
- a Forgejo Actions CI/CD example
- a documented lab book for rebuilding the cluster
- a place to capture troubleshooting and design decisions
It is also a useful personal reference. If something breaks months later, the reasoning and recovery notes are close to the code.
What This Project Is Not
This project is not a managed Kubernetes platform.
It is not intended to replace enterprise Kubernetes distributions or cloud-managed Kubernetes services.
It is also not a complete high-availability production design. The current topology uses one control plane and two worker nodes, which is fine for a homelab and learning environment, but not the same as a production HA cluster.
That distinction matters.
The value of this project is not that it creates the most advanced Kubernetes platform possible. The value is that it creates a clear, reproducible, documented foundation.
Lessons Learned and Final Thoughts
The most useful lessons from this project were not only about Kubernetes itself.
They were about the integration points around Kubernetes:
Can the runner reach Proxmox?
Are the Proxmox API permissions correct?
Does Terraform state survive outside the runner?
Are SSH keys separated by purpose?
Does the Ansible inventory match the Terraform output?
Are workflow inputs safe and predictable?
Can the cluster be accessed after deployment?
These are the areas where homelab automation often fails. A Kubernetes cluster can be installed manually in many ways, but making the full process repeatable exposes all the small details around networking, permissions, state, secrets, runners, and documentation.
That is also why the project includes a dedicated troubleshooting chapter. The problems i encountered during the initial deployment are not hidden away; they are documented next to the project so they can be reused later.
For me, the most important result is not simply that Kubernetes runs on Proxmox.
The real win is that the path to get there is now visible.
Terraform creates the VMs. Ansible and Kubespray install Kubernetes. Forgejo runs the workflows. The state is stored remotely. And the troubleshooting history stays close to the code, ready for the next time something breaks at the least convenient moment.
That makes the project much more useful than a one-time manual setup.
It turns the cluster into something I can understand, rebuild, improve, and confidently revisit later — which is exactly what I want from a homelab project.
Project Repository
The project is available here:
https://github.com/MRi-LE/proxmox-kubernetes-gitops
Useful entry points:
README.md
docs/00-lab-overview.md
docs/01-concepts.md
docs/10-troubleshooting-labs.md
docs/12-upgrades-next-steps.md