minReplicas | Minimum number of replicas in this pool. Used as the cluster-autoscaler floor. | int | 0 |
maxReplicas | Maximum number of replicas in this pool. Used as the cluster-autoscaler ceiling. | int | 10 |
instanceType | Virtual machine instance type. | string | u1.medium |
diskSize | System disk size for the worker VM. Carries the Talos OS image, kubelet state, containerd image cache, and any local-path PVCs. How the OS image reaches the disk follows this pool’s osImage: the default and osImage.factory stream the Image Factory raw artifact in over HTTP, while osImage.builtin makes the disk a storage-layer CDI clone of a shared golden image, with no HTTP fetch. A builtin pool must keep diskSize at or above the golden’s storage size (6Gi in the shipped worker image catalog), because a CDI clone target cannot be smaller than its source – the chart rejects a smaller value at render time rather than letting the clone stall. Sizing a builtin pool also has to allow for transient headroom the finished disk does not show: CDI populates a larger target by cloning the golden at the source size and then growing it, so a worker being provisioned occupies a temporary clone PVC at the golden’s size on top of this diskSize, and both are multiplied by the replica count of a replicated StorageClass. The golden itself is a separate one-time cost for the whole cluster, not a per-worker one. So the peak is the golden, plus diskSize + golden storage for each worker cloning at that moment. A pool whose storage backend cannot spare that peak does not fail: the clone waits for space, the VM never boots, and the reason is visible only in the storage layer’s own events. | quantity | 20Gi |
roles | List of node roles. Each role r labels nodes with node-role.kubernetes.io/<r>. Use [ingress-nginx] for a pool that hosts the tenant ingress-nginx controller. | []string | [] |
resources | Explicit CPU and memory for each worker node, as an alternative to instanceType sizing. Optional: when omitted, the node is sized by instanceType. When both cpu and memory are set, they take precedence and instanceType is ignored (the instancetype is omitted from the VM, since KubeVirt cannot override an instancetype’s CPU/memory). Set both cpu and memory together or neither; setting only one is rejected at render time. | object | {} |
resources.cpu | Number of vCPU cores for each worker VM, as a whole number such as 2. On the kubevirt substrate the value becomes the VM’s integer domain.cpu.cores, so millicore, fractional and negative quantities, and counts above 4294967295, are rejected at render time; an unquoted 0 is treated as unset. On the proxmox substrate a millicore or fractional quantity is rounded up to whole cores. | quantity | "" |
resources.memory | Memory (RAM) available. | quantity | "" |
podCpuLimit | CPU limit for the worker VM’s virt-launcher compute container, which is the ceiling the guest vCPU threads, the QEMU emulator thread and the IO threads share. Distinct from resources.cpu, which sizes the guest itself. Cannot be combined with instanceType sizing: KubeVirt refuses a VM that references an instancetype and also sets domain.resources.limits.cpu, so the chart rejects that combination at render time. Sizing the pool by resources (both cpu and memory) is what drops the instancetype from the VM, which is where the limit is usable. When unset, KubeVirt derives the limit from the guest vCPU count in any namespace carrying a ResourceQuota that limits CPU, so a worker sized at N vCPUs runs under a ceiling of N with the emulation overhead taken out of the same N. Setting a value declares the limit on the VM directly, which is both what lands it on the container and what stops KubeVirt deriving one. It moves the CPU request with it: KubeVirt copies a declared CPU limit into the CPU request when the VM declares no request of its own, so a pool that sets only this field asks the scheduler for its whole ceiling instead of the fraction of the vCPU count it asked for before, and a ceiling the nodes cannot spare leaves the workers Pending on Insufficient cpu. Pair it with podCpuRequest to move the ceiling without moving the request. Zero means no ceiling at all rather than the tightest one, since the kubelet turns a zero CPU limit into no CFS quota while the declared key still stops KubeVirt deriving one — but it is only accepted together with podCpuRequest: 0, because KubeVirt keeps deriving a request from the vCPU count when the VM declares none and a request above a declared limit of zero is rejected when the worker’s Pod is created. A negative value is refused at render time. Changing it rolls the pool, because the worker VM template is named by a hash of its content. Keep in sync with the parent kubernetes chart. | quantity | "" |
podCpuRequest | CPU request for the worker VM’s virt-launcher compute container, which is what the scheduler reserves for a worker on its host and what weights it against neighbours on a contended one. Distinct from resources.cpu, which sizes the guest itself. It exists to be paired with podCpuLimit: KubeVirt copies a declared CPU limit into the request when the VM declares no request, so raising the ceiling alone raises what every worker in the pool asks the scheduler for, and this field is what holds the request still while the ceiling moves. Cannot be combined with instanceType sizing, for the same reason as podCpuLimit: KubeVirt refuses a VM that references an instancetype and also sets domain.resources.requests.cpu, so the chart rejects that combination at render time, as it does a value above podCpuLimit. Usable without a limit, where it only changes the scheduler’s weighting: KubeVirt still derives the ceiling from the guest vCPU count in any namespace carrying a ResourceQuota that limits CPU, so keep the request at or below that count. A request above that derived ceiling is refused only when the worker’s Pod is created, and deliberately not at render time: the ceiling exists only where such a ResourceQuota does, so refusing here would take away a configuration that works in a namespace without one. Zero is accepted on its own and reserves nothing, but zero beside a podCpuLimit is refused, because KubeVirt cannot tell an explicit zero request from an absent one and replaces both with the limit — which is the outcome this field exists to avoid, arrived at silently. A negative value is refused as well. When unset, KubeVirt computes the request from the guest vCPU count and the cluster CPU allocation ratio. Changing it rolls the pool, because the worker VM template is named by a hash of its content. Keep in sync with the parent kubernetes chart. | quantity | "" |
gpus | List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). | []object | [] |
gpus[i].name | Name of GPU, such as “nvidia.com/AD102GL_L40S”. | string | "" |
kernelModules | Kernel modules loaded on every worker in this pool, emitted as Talos machine.kernel.modules. A Talos system extension installs a module but does not load it, so an extension-provided driver needs its modules declared here. Leave unset to let the chart decide: a pool holding at least one nvidia.com/* GPU gets nvidia (with NVreg_NvLinkDisable=1, which the gpu-operator driver container applies on non-Talos workers; on Talos that container has to be turned off or it clashes with the system extension, and nothing then mounts the ConfigMap carrying the parameter), nvidia_uvm, nvidia_drm, nvidia_modeset (that order — Talos loads the list in sequence and the last three depend on the first), and any other pool gets nothing. Set a non-empty list to replace the chart’s choice entirely, or [] to opt out and emit no modules even on a GPU pool. The field carries no default, and unset is not the same value as []: a generated parameter table that shows [] in its Default column is printing what it prints for any array with no default, not the effective value — leaving the field out selects the automatic set, writing [] disables it. The module still has to be in the image, and which extensions the image carries is the pool’s schematic: a GPU pool points osImage.factory.schematicID (or osImage.builtin.schematicID) at a schematic with the NVIDIA extensions, and on Blackwell (GB202) that must be the open-kernel-modules extension. The automatic set keys on the nvidia.com/ resource prefix alone and nothing cross-checks it against that schematic; on Talos v1.13 a declared module the image does not ship leaves its controller retrying rather than failing the boot, so the symptom is a missing driver, not a dead node. This value is part of the worker machine config, whose content hash names the TalosConfigTemplate, so changing it rolls every existing worker in the pool rather than only affecting nodes created later. The automatic set is itself such a change, on the upgrade that introduces this field, for every existing pool that holds an nvidia.com/* GPU and leaves the field unset: that pool rolls. | []object | [] |
kernelModules[i].name | Module name as modprobe takes it, e.g. nvidia_uvm. | string | "" |
kernelModules[i].parameters | Module parameters, each as a bare key=value string. | []string | [] |
kubelet | Kubelet resource reservations for this pool. | object | {} |
kubelet.systemReservedMemory | Memory reserved for host OS. Auto-computed from instanceType if empty. | string | "" |
kubelet.kubeReservedMemory | Memory reserved for kubelet and container runtime. Auto-computed from instanceType if empty. | string | "" |
kubelet.systemReservedCpu | CPU reserved for host OS. Auto-computed from instanceType if empty. | string | "" |
kubelet.kubeReservedCpu | CPU reserved for kubelet and container runtime. Auto-computed from instanceType if empty. | string | "" |
kubelet.evictionHardMemory | Hard eviction threshold for memory (absolute like 200Mi or percentage like 7%). | string | 7% |
kubelet.evictionSoftMemory | Soft eviction threshold for memory (absolute like 1Gi or percentage like 10%). | string | 10% |
logSerialConsole | Stream each worker VM’s guest serial console into a guest-console-log container beside virt-launcher, readable with kubectl logs. The only view of a worker that stalls during boot before Talos apid answers, when neither a Node nor a certificate request exists to diagnose from. Two costs the field name does not show. Changing the value rolls the pool, because it is part of the worker VM template whose name is a hash of its content – including the stuck VM whose console was wanted, so it buys visibility into the next boot rather than the current one. And it overrides the platform’s cluster-wide disableSerialConsoleLog, set because that container has been seen holding virt-launcher in PodInitializing (kubevirt/kubevirt#15989), which would take the pool down rather than explain it – so check that virt-launcher Pods reach Running after enabling it. | bool | false |
maxUnhealthy | Maximum number of unhealthy nodes tolerated in this pool before remediation is paused. The MachineHealthCheck admission webhook accepts either a bare integer (“0”, “1”, …) or a percentage (“0%”, “50%”); bare numeric strings are rejected, so the safer default is a percentage. Drop to “0%” once the pool is stable. Watch how a percentage behaves in a small group: the MachineHealthCheck controller scales it against the number of machines the check covers and rounds down, then remediates only while the unhealthy count is at or below that result. At “50%” a group of one therefore never remediates, because half of one rounds down to zero, and a group of two remediates one unhealthy machine but not two. | string | 50% |
nodeStartupTimeout | Maximum time a Machine is allowed to spend reaching the Ready condition before it is remediated. Raise for slow first boots (Talos image fetch from factory.talos.dev or a busy storage class on the kubevirt-csi PVC populator). The MachineHealthCheck measures it from the later of the Machine’s creation and its InfrastructureReady transition, so the clock restarts when the infrastructure reports ready: a worker gets this long for the disk image import and the VM to come up, and then this long again for the guest to boot and its kubelet to register with the tenant apiserver. Either leg stretches on a host under load, and a Machine remediated here is deleted before it ever joins, so its replacement starts the same sequence from the beginning. | string | 20m |
readyUnknownTimeout | How long a worker Node may report Ready=Unknown before the MachineHealthCheck remediates the Machine, which for a KubeVirt worker means deleting the VM. Ready=Unknown is what the node lifecycle controller writes once it stops hearing from a kubelet, on a grace period of its own, so the wall clock from a guest going quiet to a deleted worker is this value plus that grace period. A guest starved of CPU stops answering long before it is dead, so a value of a few tens of seconds deletes workers that would have come back on their own. Validated at render time like maxNodeProvisionTime: the CAPI webhook does not check these timeouts, and a zero or negative value would remediate the moment the condition flips. Keep in sync with the parent kubernetes chart. | string | 5m |
readyFalseTimeout | How long a worker Node may report Ready=False before the MachineHealthCheck remediates the Machine. A Node that has registered but has no working CNI yet reports Ready=False, so this value is the whole budget a first boot gets for the CNI rollout to reach that node, and a worker deleted mid-rollout takes the rollout with it while its replacement starts over. Raising it delays replacing a worker that is genuinely dead, but not rescheduling its workload: Pods leave an unreachable Node on the node lifecycle controller’s taint-based eviction and the toleration that carries, not on this timer. Validated at render time like maxNodeProvisionTime: the CAPI webhook does not check these timeouts, and a zero or negative value would remediate the moment the condition flips. Keep in sync with the parent kubernetes chart. | string | 10m |
maxNodeProvisionTime | How long the cluster-autoscaler waits for a newly created worker to register as a Node before it treats that machine as long-unregistered. Rendered onto this pool’s MachineDeployment as the cluster.x-k8s.io/autoscaling-options-maxnodeprovisiontime annotation, which overrides the autoscaler’s built-in default for this pool only. Two things change when the timer fires, and both make a slow join worse rather than better: the machine stops counting towards the pool’s upcoming capacity, so the autoscaler asks for a replacement for capacity that is already on its way, and the machine becomes eligible for removal. Size it above the slowest join a healthy worker can take, counting the Talos image import, the guest boot and the CNI rollout. Accepted as a whole number of seconds, minutes or hours (“30m”, “1h30m”), which is narrower than Go’s duration syntax on purpose: the autoscaler ignores a value it cannot parse and falls back to its own default without failing, and the narrower shape cannot express a value that overflows the parser or a fraction it rounds to zero. The canonical form a duration serializes back to (“20m0s”, “1h0m0s”) is accepted. Keep in sync with the parent kubernetes chart. | string | 30m |