Replace the dmacvicar/libvirt provider + resources with the modern bpg/proxmox provider. The dev cluster moves off the memory-constrained local libvirt host onto the Proxmox VE box. - versions.tf: pin bpg/proxmox ~> 0.66; provider reads auth from env (PROXMOX_VE_*, token never a tofu var) and normalizes a schemeless PROXMOX_VE_ENDPOINT (prepend https://) to avoid the :8006 HTTP 301. - variables.tf: Proxmox placement vars (node=proxmox, datastore=Storage, image_datastore=local, bridge=vmbr0); LAN 192.168.1.0/24 gw .1 DNS .18; node_ips .50/.51/.52; sizing 6 vCPU / 16384 MiB / 40+100 GiB; cpu_type host; Cilium VIP range .53-.60. - main.tf: proxmox_virtual_environment_download_file (cloud image -> local), per-node user-data snippet (proxmox_virtual_environment_file), and proxmox_virtual_environment_vm per node via name-keyed for_each (imported OS disk virtio0, blank data disk virtio1, native initialization ip_config + dns + user_data_file_id). - remove moved.tf (libvirt-specific count->for_each state migration). Authoring only; no apply. Traces R-ENV-01, R-PLAT-01. Co-Authored-By: Claude Opus 4.8 <[email protected]>
infra/tofu — dev/test cluster nodes (OpenTofu + libvirt)
Phase 1 T-1.1. Provisions the "Create" lifecycle stage (docs/02): a dedicated NAT network and 3 cloud-init'd KVM VMs, each with a thin OS disk and a blank thin Longhorn data disk. Authoring only — nothing here has been applied.
Serves R-ENV-01/02, R-PLAT-01, ADR-0001 (k3s substrate), and the network half of the LOCKED PHASE1-SCOPE decisions.
What it builds (defaults = LOCKED decisions)
| Resource | Value |
|---|---|
| NAT network | k3s-dev, 10.73.0.0/24, gateway 10.73.0.1, DHCP off (static IPs) |
| Nodes | k3s-dev-1/2/3 at 10.73.0.11/.12/.13 |
| Per node | 4 vCPU, 6 GiB RAM, host-passthrough CPU (nested virt for KubeVirt) |
| OS disk | 20 GiB thin qcow2, backed by the base cloud image |
| Data disk | 40 GiB blank thin qcow2 → /dev/vdb, mounted at /var/lib/longhorn by cloud-init |
| Seed | libvirt_cloudinit_disk wired to ../cloud-init/ templates |
| Reserved | 10.73.0.240–.250 kept out of any DHCP range for Cilium L2 ingress VIPs |
Base OS image is a variable (image_source), defaulting to Debian 12
genericcloud amd64. All sizing/IPs/counts are variables — see variables.tf.
Files
versions.tf— OpenTofu1.9.0pin +dmacvicar/libvirt ~> 0.8.1provider pin.variables.tf— every knob (counts, sizing, IPs, image, SSH key).main.tf— network, base/OS/data volumes, cloud-init seed, domains.outputs.tf— node IPs/names,primary_node_ip,k3s_api_endpoint, and a renderedansible_inventoryforinfra/ansible/.terraform.tfvars.example— copy toterraform.tfvars; onlyssh_authorized_keyis required.
Usage (operator, in the dev container — NOT run here)
cd infra/tofu
cp terraform.tfvars.example terraform.tfvars # set ssh_authorized_key
tofu init
tofu plan
tofu apply
# hand the node IPs to Ansible:
tofu output -raw ansible_inventory > ../ansible/inventory/hosts.ini
Hand-off contract
primary_node_ip/k3s_api_endpoint(10.73.0.11:6443) is the value the Ansible Cilium bootstrap and the Fluxplatform/ciliumHelmRelease must BOTH use fork8sServiceHost/k8sServicePort(docs/02 parity contract).- Destroy/rebuild proof (T-1.5):
tofu destroy -target=libvirt_domain.node[1]thentofu applyre-creates that node; Ansible re-joins it; Longhorn replicas reconverge (nodes are cattle — docs/02 statelessness).
Static validation (PM, in dev container)
tofu fmt -check -recursive
tofu init -backend=false
tofu validate