Terraform – Kubernetes VMs
Provisions the Kubernetes node VMs on Proxmox for the homelab Kubernetes cluster.
Overview
This Terraform project clones the ubuntu-noble-cloud template into virtual machines
on the proxmox nodes, following a capacity-aware placement algorithm:
- Control plane (master) VMs are spread one per node, on the nodes with the most
free memory that still fit the role's
ram. - Pack roles (worker) are water-filled over the remaining capacity, most-available node first, without a per-node limit.
Each VM is tagged with the environment (vm_env) and registered in a Terraform-backed
Ansible inventory (via the ansible provider), so the k8s_node playbook can target
them by role group.
Placement details
- Online Proxmox nodes are ordered by available memory (most available first, node name as tie-break).
- The control-plane role must fit entirely on distinct nodes with
free_mib >= ram. - Workers are packed into residual capacity (
free_mib - ram_per_cp_vm_on_node), again most-available first. - Naming:
kub-<env>-m<NN>for masters andkub-<env>-w<NN>for workers, e.g.kub-prod-m01,kub-prod-w01.
Deployment is blocked by check blocks when the fleet cannot be placed:
- no online node is available,
- not enough nodes have the free memory required for the control-plane role,
- residual capacity cannot fit all worker VMs.
Prerequisites
- Proxmox with:
- a cloneable template named
ubuntu-noble-cloud(from thepve_vmmodule), - a
ceph-workloaddatastore, - VLAN bridge
vlan<id>(defaultvlan66), - an API token and an SSH user/password with rights to create VMs on the nodes.
- a cloneable template named
- Terraform with the
bpg/proxmoxandansible/ansibleproviders (init downloads them).
Input variables
| Variable | Type | Required | Default | Description |
|---|---|---|---|---|
proxmox_endpoint |
string | yes | – | Proxmox API URL |
proxmox_api_token |
string | yes | – | Proxmox API token (secret) |
proxmox_ssh_username |
string | yes | – | Proxmox SSH user for placement (secret) |
proxmox_ssh_password |
string | yes | – | Proxmox SSH password (secret) |
vm_env |
string | no | "prod" |
Environment, part of the VM name and tags |
master_count |
number | no | null → vm_attr default |
Number of master VMs |
master_cpu |
number | no | null → vm_attr default |
vCPU cores for masters |
master_ram |
number | no | null → vm_attr default |
Master RAM in GiB (converted to MiB internally) |
master_vlan |
number | no | null → vm_attr default |
VLAN for masters |
worker_count |
number | no | null → vm_attr default |
Number of worker VMs |
worker_cpu |
number | no | null → vm_attr default |
vCPU cores for workers |
worker_ram |
number | no | null → vm_attr default |
Worker RAM in GiB |
worker_vlan |
number | no | null → vm_attr default |
VLAN for workers |
vm_attr |
map(… ) | no | see below | Full per-role VM defaults |
The vm_attr map holds the fallback attributes used when the corresponding
master_*/worker_* override is null:
vm_attr = {
master = { ram = 2048, cpu = 2, vlan = 66, count = 3 }
worker = { ram = 4096, cpu = 2, vlan = 66, count = 3 }
}
ramvalues insidevm_attrare MiB; themaster_ram/worker_ramoverrides are set in GiB and converted internally (× 1024). The placement math always works in MiB.
Usage
terraform init
terraform plan
terraform apply
The vm_ip output maps VM name to assigned IP and feeds the Ansible inventory
(inventories/) of the k8s_node playbook.
File reference
| File | Purpose |
|---|---|
main.tf |
Placement algorithm, pve_vm module wiring, checks |
variables.tf |
Clusters, Proxmox credentials, per-role defaults/overrides |
provider.tf |
proxmox and ansible provider configuration |