I wanted to build a Kubernetes platform myself, from the hardware up, instead of consuming a managed one.
Everything here is set up manually before any Infrastructure as Code. The point is to understand each layer before writing the tool that replaces me.
Dual Xeon Silver 4116, 128 GB RAM, running Proxmox VE. Key-only SSH, deny-by-default firewall, TOTP on the root account.
Proxmox runs from a dedicated external SSD, booted on demand. The Windows install on the internal drives stays untouched. No dual boot, no partitioning. It also means the lab is not running around the clock: every session starts with a full boot of the cluster, which makes it a reproducibility test as much as a lab.
Three Debian VMs provisioned from the Proxmox CLI with cloud-init. One server, two agents, SQLite datastore.
Traefik as ingress controller, cert-manager issuing and renewing Let's Encrypt certificates over the DNS-01 challenge on Route 53. The cluster stays private, but every service is reachable over HTTPS on a real domain, with an IAM user scoped to a single hosted zone.
Gitea deployed via Helm as the internal forge, ArgoCD watching a single repository as the source of truth and reconciling the cluster against it.
Gitea Actions enabled, a self-hosted Gitea Runner registered against the organisation in rootless Docker-in-Docker mode, with per-job container isolation.
Privileged Docker-in-Docker gives the pod near-total access to the host, so it was out. A pod-per-job runner is the cloud-native answer, but it ships only with the Enterprise edition. That leaves rootless DinD: no privileged pod, real isolation between jobs.
Image builds pushed to a registry, vulnerability scanning and signing before deployment. Buildah in rootless mode is the next step, once the daemon-based path is fully understood.
Prometheus and Grafana, on a node outside the cluster they monitor.
Terraform and Ansible, once the manual flow is fully understood.
How the stack is built, from the disk it boots off to the cluster on top.
How a change reaches the cluster. CI writes to Git and to the registry, and stops there.