0077. Pod and service subnets, the CNI Talos runs and the install disk are spec fields rendered per contract

Status: Accepted · Date: 2026-10-26 · Design section: Nodes (approval, disk roles), CNI, Bootstrap

Decided: TalosCluster.spec.network {podSubnets, serviceSubnets} (optional, fixed once set) and TalosMachine.spec.install {disk, image} carry the values ADR 0075 could not express; the system layer renders them as the v1.14 documents (KubeNetworkConfig, UnattendedInstallConfig) from that contract and as the legacy fields before it, and drops Talos's own CNI (the KubeFlannelCNIConfig document, or cluster.network.cni.name: none) for every preset but flannel; approval (ichor machine approve --install-disk, the UI form) sets the install disk, ichor init stops writing machine.install, hostnames are RFC 1123 checked, and adopt names the spec field for each such drift path. This is option C of ADR 0075 with the per-node half also as a spec field, not a patch kind.

Context

ADR 0075 left open how a v1.14 cluster gets non-default subnets, an install disk and "no flannel". At the v1.14 contract the generator emits its defaults, no spec field carried those values, and the legacy Config fields conflict with the v1.14 documents, so a correct cluster could not be built, adopted, or have a maintenance node installed.

Decision

  1. TalosCluster.spec.network.podSubnets / serviceSubnets: lists of CIDRs, at most two each (one per family), valid CIDR by CEL (isCIDR). An unset list keeps Talos's default (10.244.0.0/16, 10.96.0.0/12). The whole field has self == oldSelf: it cannot change once set. A cluster that never declared it may set it once, so an adopted cluster can record what it already runs; setting it on a running cluster to something else renders a different KubeNetworkConfig, which is reboot-required and refused by the machine controller (below). pkg/render sets the generator's own PodNet/ServiceNet, so machinery chooses the form: the KubeNetworkConfig document at v1.14 and cluster.network.podSubnets/serviceSubnets before it.
  2. CNI. For a preset other than flannel (cilium, custom) the system layer removes Talos's CNI: at v1.14 the KubeFlannelCNIConfig document, below it cluster.network.cni.name: none. Before this change the cilium preset did neither below v1.14 either (its patch only sets the kube-proxy flag), so the two contracts now agree. A custom preset's own documents are patches and still win. An unset preset is flannel (the CRD default).
  3. TalosMachine.spec.install.disk selects the system disk by exactly one of wwid, serial, byIDPath, busPath (the design's fallback chain) or devPath (last resort, the kernel can renumber it); install.image is the installer image, empty meaning the booted schematic's. disk is required inside install: the v1.14 document is invalid without a selector. At v1.14 it renders UnattendedInstallConfig with a CEL selector checked against Talos's disk environment (disk.wwid == "...", disk.serial == "...", "/dev/disk/by-id/x" in disk.symlinks, disk.bus_path == "...", disk.dev_path == "...") and wipe: false (what the generator writes). Before v1.14 it renders machine.install (diskSelector.wwid|serial|busPath; installDisk for a by-id or device path, because the legacy selector has no symlink match). A machine with no install renders none, as before.
  4. Approval. ApproveMachineRequest.install and Machine.install (proto), ichor machine approve --install-disk <wwid:ID | serial:S | /dev/disk/by-id/... | /pci... | /dev/sdX> [--install-image]. The UI form lists the disks that GetNode already reads from a maintenance-mode machine (Disk.dev_path is new on the wire) and picks the most stable identifier each reports; if the machine reports no disks (Talos API unreachable) it falls back to a typed device path and says so. ichor init --install-disk takes the same syntax and writes spec.install on the first machine; its bootstrap patch keeps only machine.type and machine.certSANs (the patch CRD requires them).
  5. Hostname: TalosMachine.spec.hostname is an RFC 1123 subdomain (CRD pattern and 253 limit), checked again in ApproveMachine (InvalidArgument with the reason) and in the UI form. This rejects "てすと".
  6. adopt reports, for each drift path it cannot patch, the spec field that carries it: KubeNetworkConfig.podSubnets / cluster.network.podSubnets -> spec.network.podSubnets (service likewise), UnattendedInstallConfig.* / machine.install.* -> spec.install, KubeFlannelCNIConfig.* / cluster.network.cni -> spec.cni.preset. adopt still does not write those fields itself: they live on the TalosCluster and TalosMachine, which the user owns.

Hash and reboot impact on existing clusters

The rendered hash is unchanged for a flannel cluster, for any cluster with no spec.network and no spec.install, and for a machine with neither. It changes in two cases.

  • A v1.14 cluster with a non-flannel preset: the rendered config no longer has KubeFlannelCNIConfig. The diff has KubeFlannelCNIConfig.* paths, which pkg/fieldmeta classifies as reboot-required (no rule, the safe default). I did not verify that removing the document is reboot-free on a node and did not guess it. The machine controller never reboots by itself: refuseReboot sets Ready false and leaves the node as it is. So there is no surprise reboot, but such a machine reports "needs a reboot" until the operator applies the new config (talosctl apply-config --mode no-reboot after checking, or a planned reboot). The flannel DaemonSet Talos created at bootstrap is not pruned by Talos on its own either way. Talos's own flannel next to Cilium was the fault being fixed.
  • A pre-1.14 cluster with a non-flannel preset gains cluster.network.cni.name: none: path cluster.network.cni.name, also reboot-required by default, same outcome.
  • Setting spec.install on a machine that has none: v1.14 adds UnattendedInstallConfig, pre-1.14 changes machine.install. Both are classified install-time-only (new rule UnattendedInstallConfig, next to machine.install), so the controller reports "needs a reset and reinstall, not an apply" and applies nothing: the field is for approval of a node that has not installed yet, which receives the whole config at first apply.
  • A cluster whose bootstrap patch still carries machine.install (written by an older ichor init) keeps working while its machine has no spec.install. Setting both at v1.14 fails validation inside Talos (UnattendedInstallConfig conflicts with machine.install), and the render error says so; remove the legacy field from the patch first.

Not done

  • Cert SANs, the DNS domain, install wipe and extra kernel arguments still have no field (certSANs stays in the init bootstrap patch as a legacy Config; whether that is accepted next to the v1.14 documents was not re-checked here).
  • No TalosOperation resets a node to apply a changed install disk.
  • The disk list in the approval form comes from GetNode, so for a machine that no one can reach it is a typed path.
  • Whether removing KubeFlannelCNIConfig is no-reboot in Talos v1.14.3 is unverified; it would let that rule be safe.

Search the docs

Search pages and headings