Terraform clones a cloud-init template into Proxmox VMs that become k3s control-plane and worker nodes. Each VM gets a fixed IP, an SSH key, and Proxmox tags so k3s-ansible (or similar) can target it. After apply, you install k3s on those VMs with Ansible or another method. Placeholders such as <subnet> and <first-control-plane-ip> stand in for your network.
This root is only the k3s VMs (kubernetes/terraform/). The CKS kubeadm lab is two Ubuntu VMs you create yourself (typically a full clone of template 9999 in the Proxmox UI), then kubeadm by hand. It is not this Terraform directory. Do not terraform apply here to create those two exam nodes.
Overview#
The layout is three control-plane VMs (API server and etcd) and two worker nodes. All five are full clones of the cloud-init template (VM ID 9999 in the examples). They share an SSH user and public key, and they carry tags k3s plus control-plane or node.
| Role | VM names | Count | IP range (example) | Tags |
|---|---|---|---|---|
| Control plane | k3s-control-plane-01 … 03 | 3 | <subnet>.201–203/24 | k3s, control-plane |
| Node | k3s-node-01 … 02 | 2 | <subnet>.211–212/24 | k3s, node |
Set the Proxmox host name in Terraform (for example proxmox-pve or a variable). Each VM uses one virtio NIC on a single bridge such as vmbr0, with your LAN gateway (for example <subnet>.1). Prefer host-local SSD (local-lvm) for guest OS disks when the cluster will run Longhorn, not NAS-backed boot disks, and not Ceph for this generation.
Replace <subnet> with your actual subnet (for example 192.168.1). Adjust IP ranges, node name, and bridge in the Terraform variables or in main.tf.
Prerequisites#
You need a Proxmox VE host with API access (https://your-proxmox-host:8006), the cloud-init template VM (ID 9999; create it with the Proxmox cloud-init template guide), Terraform >= 1.0, and an SSH key pair for the cloud user (path in Terraform, for example ~/.ssh/<cloud-init-key>). The same key fetches kubeconfig and runs Ansible later. Authenticate to Proxmox with username and password; an API token works when the provider supports it.
File layout#
The Terraform code lives in the homelab repo under kubernetes/terraform/:
| File | Purpose |
|---|---|
main.tf | Proxmox VM resources: k3s control-plane VMs and nodes (clone, CPU, memory, network, cloud-init) |
providers.tf | Terraform and Proxmox provider (bpg/proxmox) configuration |
variables.tf | Input variables: Proxmox endpoint, username, password |
terraform.tfvars.example | Example values; copy to terraform.tfvars and fill in. Optional gpu_passthrough_pci_ids stays empty unless you are provisioning a new worker with PCI passthrough. Live clusters should attach a GPU with qm set as in GPU Operator, not terraform apply. |
Provider and variables#
The provider is bpg/proxmox (~> 0.66 in the snippet below). Authentication is the endpoint URL, username, and password (sensitive). A comment in the code notes a future switch to an API token.
| Variable | Description | Example |
|---|---|---|
proxmox_endpoint | Proxmox API URL | https://your-proxmox-host:8006 |
proxmox_username | Proxmox user | root@pam or your-username@pam |
proxmox_password | Proxmox password | (sensitive) |
Step-by-step usage#
1. Clone the repo and enter the Terraform directory#
cd /path/to/homelabs
cd kubernetes/terraform2. Copy and edit tfvars#
cp terraform.tfvars.example terraform.tfvarsEdit terraform.tfvars and set proxmox_endpoint to your Proxmox API URL (for example https://<proxmox-host>:8006), proxmox_username (for example root@pam), and proxmox_password. Do not commit terraform.tfvars; add it to .gitignore if needed.
3. Ensure the cloud-init template and SSH key exist#
Template VM ID 9999 must exist and be a full-cloneable template (see Proxmox cloud-init template). Place the public key where Terraform file() expects it, or update that path in main.tf.
4. Initialize and apply#
terraform init
terraform plan
terraform applyConfirm when prompted. Terraform creates three control-plane VMs and two nodes on the configured Proxmox host.
5. Deploy k3s on the new VMs#
Use your preferred method (for example k3s-ansible) with the same SSH user and key you provisioned on the VMs. The playbook inventory groups remain [master] for control-plane VMs and [worker] for nodes; those are the names the playbook expects.
After the playbooks run, copy kubeconfig from the first control-plane node (replace <first-control-plane-ip> with that VM’s IP):
scp -i ~/.ssh/<cloud-init-key> <ssh-user>@<first-control-plane-ip>:~/.kube/config ~/.kube/configThen verify with kubectl get pods --all-namespaces.
For remote access with GitHub OIDC login (recommended), configure Dex OIDC and use homelabs-private/clusters/<cluster>/kubeconfig-oidc.yaml.template instead of relying on client certificates alone.
Destroying the VMs#
To remove all Terraform-managed k3s VMs:
cd kubernetes/terraform
terraform destroyConfirm when prompted. This does not remove the template VM (9999) or any resources not managed by this Terraform configuration.