- HCL 93.9%
- Go Template 6.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| ansible | ||
| terraform | ||
| .gitignore | ||
| README.md | ||
Proxmox Kubernetes Lab
Provisions VMs on Proxmox with Terraform, then bootstraps a vanilla
kubeadm Kubernetes cluster with Cilium networking using Ansible.
Either Rocky Linux or Ubuntu can be used as the guest OS — the Ansible
roles detect the distro automatically (ansible_facts['os_family']) and
use dnf/SELinux/firewalld on Rocky or apt/ufw on Ubuntu, so no extra
configuration is needed beyond pointing template_vm_id at the template
you want to clone.
Prerequisites
- A Rocky Linux or Ubuntu template already built in Proxmox, with
qemu-guest-agentand cloud-init installed (the template's VM ID is all Terraform needs). - A Proxmox API token with permission to clone/create/start VMs.
terraform>= 1.6,ansible>= 2.15, and an SSH key pair.- Set
ssh_usernameinterraform.tfvarsto match the template's cloud-init user convention (e.g.rockyfor Rocky Linux,ubuntufor Ubuntu). - The template's default virtio NIC name (
eth0vsens18) — boot one VM from the template and check withip -o linkif you're not sure; setkube_vip_interfaceinansible/group_vars/all.ymlto match.
1. Provision the VMs (Terraform)
cd terraform
cp terraform.tfvars.example terraform.tfvars
# edit terraform.tfvars: Proxmox connection, template_vm_id, network, etc.
terraform init
terraform apply
control_plane_count and worker_count are left unset in the example
tfvars on purpose — terraform apply will prompt for them interactively:
- Control-plane count —
1(single control plane) or3(HA control plane, validated; anything else is rejected and re-prompted). - Worker count — any number from
1to5.
Each VM gets a random, role-tagged name (e.g. k8s-cp-fluent-panther,
k8s-worker-brave-otter) and a static IP from network_cidr starting at
ip_start. The address at ip_start - 1 is reserved for the kube-vip VIP
used in the HA case — leave it free on your network.
On success, Terraform writes ansible/inventory/hosts.ini for you,
grouped into control_plane_primary, control_plane_secondary,
control_plane, workers, and kubernetes.
2. Bootstrap Kubernetes (Ansible)
cd ansible
ansible-galaxy collection install -r requirements.yml
ansible-playbook site.yml
This will, across all nodes:
- Disable swap; on Rocky, set SELinux to permissive and disable firewalld;
on Ubuntu, disable ufw (lab-only shortcut — replace with explicit
firewall rules for anything beyond a lab); load
overlay/br_netfilter, and set the required sysctls. - Install and configure
containerd(systemd cgroup driver) andkubelet/kubeadm/kubectlfrom the pinnedpkgs.k8s.iorepo forkubernetes_version(viadnfon Rocky,apton Ubuntu).
Then, control-plane-specific:
- If
control_plane_count = 3: deploykube-vipas a static pod on every control-plane node for API-server HA, ARP mode, beforekubeadm init. - Run
kubeadm initon the primary control plane (API endpoint = the VIP in the HA case, or the node's own IP for a single control plane). - Join the remaining control planes with
kubeadm join --control-planeand a freshly generated certificate key. - Install Cilium via the
ciliumCLI. - Join all worker nodes with
kubeadm join.
3. Verify
ssh <ssh_username>@<control_plane_primary_ip>
kubectl get nodes -o wide
cilium status
Notes / things to adjust before using this beyond a lab
- Firewalld (Rocky) / ufw (Ubuntu) is disabled outright rather than opening the specific kubeadm/Cilium ports — fine for an isolated lab network, not for anything exposed further.
kubeadm token create --print-join-commandtokens expire after 24h; re-run the relevant Ansible role (orkubeadm token createby hand) if you provision workers well after the control plane.