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
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 hasself == 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 differentKubeNetworkConfig, which is reboot-required and refused by the machine controller (below).pkg/rendersets the generator's ownPodNet/ServiceNet, so machinery chooses the form: theKubeNetworkConfigdocument at v1.14 andcluster.network.podSubnets/serviceSubnetsbefore it.- CNI. For a preset other than
flannel(cilium,custom) the system layer removes Talos's CNI: at v1.14 theKubeFlannelCNIConfigdocument, below itcluster.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 owndocumentsare patches and still win. An unset preset is flannel (the CRD default). TalosMachine.spec.install.diskselects thesystemdisk by exactly one ofwwid,serial,byIDPath,busPath(the design's fallback chain) ordevPath(last resort, the kernel can renumber it);install.imageis the installer image, empty meaning the booted schematic's.diskis required insideinstall: the v1.14 document is invalid without a selector. At v1.14 it rendersUnattendedInstallConfigwith 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 == "...") andwipe: false(what the generator writes). Before v1.14 it rendersmachine.install(diskSelector.wwid|serial|busPath;installDiskfor a by-id or device path, because the legacy selector has no symlink match). A machine with noinstallrenders none, as before.- Approval.
ApproveMachineRequest.installandMachine.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 thatGetNodealready reads from a maintenance-mode machine (Disk.dev_pathis 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-disktakes the same syntax and writesspec.installon the first machine; its bootstrap patch keeps onlymachine.typeandmachine.certSANs(the patch CRD requires them). - Hostname:
TalosMachine.spec.hostnameis an RFC 1123 subdomain (CRD pattern and 253 limit), checked again inApproveMachine(InvalidArgumentwith the reason) and in the UI form. This rejects "てすと". adoptreports, 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.adoptstill 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 hasKubeFlannelCNIConfig.*paths, whichpkg/fieldmetaclassifies 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:refuseRebootsets 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-rebootafter 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: pathcluster.network.cni.name, also reboot-required by default, same outcome. - Setting
spec.installon a machine that has none: v1.14 addsUnattendedInstallConfig, pre-1.14 changesmachine.install. Both are classified install-time-only (new ruleUnattendedInstallConfig, next tomachine.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 olderichor init) keeps working while its machine has nospec.install. Setting both at v1.14 fails validation inside Talos (UnattendedInstallConfigconflicts withmachine.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
KubeFlannelCNIConfigis no-reboot in Talos v1.14.3 is unverified; it would let that rule be safe.