This play builds 1 control plane + 3 worker VMs on libvirt/KVM, then provisions k0s with Calico as the CNI and automatically installs Traefik and cert-manager via OpenTofu/Terraform in a single phase.
- Libvirt + KVM installed and running (qemu:///system)
tofu(OpenTofu) orterraforminstalledk0sctlinstalled on the host- A cloud image on disk (for example
noble-server-cloudimg-amd64.img) should be available at:/var/lib/libvirt/images/noble-server-cloudimg-amd64.img
The deployment is fully automated in a single apply. OpenTofu creates the VMs, runs k0sctl, generates the kubeconfig, and then installs Traefik and cert-manager.
-
Copy the example variables file and edit values:
cp terraform.tfvars.example terraform.tfvars
-
Update
terraform.tfvars:- Set
base_imagepath. - Add your
ssh_authorized_keys. - Set
cloudflare_api_tokenandacme_email.
- Set
-
Initialize and Apply:
tofu init tofu apply
- Infrastructure: creates VMs and networking.
- Cluster: runs
k0sctland writeskubeconfig. - Providers: Kubernetes/Helm providers load the generated kubeconfig.
- Add-ons: installs Traefik, cert-manager, and the ClusterIssuer.
traefik-values/: Contains the values.yaml for the Traefik Helm chart.cert-manager-config/: Contains the values.yaml and the ClusterIssuer manifest.kubeconfig.placeholder: A dummy config used by providers during the initial plan before the real cluster exists.
If you need to manually interact with the cluster:
# Export the generated kubeconfig
export KUBECONFIG=./kubeconfig
# Check node status
kubectl get nodes -o wide
# Check pods
kubectl get pods -A- Persistence: The
kubeconfig.placeholderensurestofu planworks on fresh clones. - Provider Switching: The Kubernetes/Helm providers read the generated kubeconfig once it exists.
- Network: Static IPs are configured via cloud-init. Default CIDR is
192.168.150.0/24.