kubeadm sysext
The opt-in kubeadm extension provides kubelet, kubeadm, kubectl, crictl, containerd, runc, and CNI plugins. Use k0s instead for a k0s cluster; the two Kubernetes versions move independently.
Enable the extension
kubeadm_<image-version>.raw.zst is image-locked and included in the signed
release checksums. On installed nodes, enable the kubeadm
sysupdate feature. Enabling the
feature alone does not fetch the running version; see
extension fetching.
Diskless provisioning writes /etc/extensions/kubeadm_<ver>.raw with a sha256
verification hash.
No services are enabled by default. A worker's Ignition must enable the
extension, containerd.service, and kubelet.service, then run the cluster's
kubeadm join command. For a control plane, use the procedure below.
Versions
Check the shipped Kubernetes version in the upstream
include/kubeadm.yml
before joining or updating:
kubeadm joinmust use the minor that last rankubeadm initorkubeadm upgradeon the cluster.- kubelet may be older than the API server, never newer.
- Patch updates do not change these constraints. Component patches are tracked automatically upstream; Kubernetes minor bumps are manual and follow the cluster control-plane upgrade.
The extension updates with the OS. Keep its image version matched to the booted OS, including on rollback. See the upstream version-update procedure for changing Kubernetes and runtime pins.
Runtime contract
- Before containerd starts, its unit seeds
/etc/containerd/config.tomland/etc/crictl.yamlonly if absent, migrates the imports line, loadsoverlayandbr_netfilter, and applies forwarding and bridge sysctls. These steps run after the sysext merge, before kubeadm preflight. - containerd stores data in
/var/lib/containerdand waits for any per-node mount there. It usesSystemdCgroup = true, sandbox imageregistry.k8s.io/pause:3.10.1, and registry config in/etc/containerd/certs.d. - The config imports
/usr/share/bluefin/containerd/conf.d/*.tomlfrom extensions and/etc/containerd/conf.d/*.tomlfor local overrides. Drop-ins deep-merge plugin sections; empty globs are harmless. The NVIDIA toolkit adds its runtime this way. /opt/cni/binstays writable for the cluster CNI. containerd searches it before the shipped/usr/libexec/cniplugins.- kubelet retries every 10 seconds until
kubeadm joinwrites/var/lib/kubelet/config.yaml. Its volume-plugin directory is/var/lib/kubelet/volumeplugins, not read-only/usr. - kubelet uses systemd-resolved's
/run/systemd/resolve/resolv.conf; the base/etc/resolv.conflinks there. - While kubelet runs or restarts, automatic OS and boot-deadline rollback
reboots stand down. kured owns reboots; the deadline sets
/run/reboot-required. See Updates.
Migration
On containerd start, /usr/libexec/bluefin-kubeadm-containerd-migrate adds the
shipped top-level imports line to older /etc/containerd/config.toml files.
Missing files and configs already containing a top-level imports are left
untouched. An imports inside a [table] is not top-level and does not count.
The helper validates a sibling config.toml.new.<pid> as TOML before copying
it over the original, preserving mode, owner, and SELinux label. If validation
or copying fails, the complete candidate remains at the logged path.
containerd still starts with the old config, which may lack extension
runtimes. Check this when the NVIDIA handler is missing. Fresh installs and
diskless boots already seed the imports line.
The NVIDIA toolkit's activation restarts containerd after merging, so the handler is loaded on the same boot. If you change the config later, restart containerd yourself.
Single-node control plane
kubeadm-init.service creates a one-node cluster that also runs workloads.
It is opt-in and does not seed configuration on workers.
-
Enable the unit. On an installed node, after the extension is merged:
systemctl enable kubeadm-init.serviceFor diskless Ignition, write the extension image and this symlink:
/etc/systemd/system/multi-user.target.wants/kubeadm-init.service -> /usr/lib/systemd/system/kubeadm-init.serviceIgnition's
enabled: trueis insufficient: it runs in the initrd before the sysext unit exists. After the merge,bluefin-sysext-activate.servicestarts the linked unit. -
Set configuration before first init. The unit seeds
/etc/kubernetes/bluefin/init.yamlfrom/usr/share/bluefin/kubeadm/init.yamlonly if absent. Write your own/etccopy through Ignition or by hand to override it. Defaults are kubeadmv1beta4, the shipped Kubernetes version, containerd's CRI socket,cgroupDriver: systemd, clusterbluefin, pod CIDR10.244.0.0/16, service CIDR10.96.0.0/12, andregistry.k8s.io. kubeadm chooses the hostname and default-route interface for the node name and advertise address. -
Allow first-boot setup. After networking, containerd, and config seeding, the unit runs only if
admin.confis absent and the init config exists. It enables containerd and kubelet, then runs:kubeadm init --config /etc/kubernetes/bluefin/init.yaml --skip-phases=addon/kube-proxyIt links
/root/.kube/configto/etc/kubernetes/admin.conf, removes the control-planeNoScheduletaint, and removesnode.kubernetes.io/exclude-from-external-load-balancersonly when this is the cluster's sole node, allowing MetalLB to announce from it.Failed init runs
kubeadm reset --forceso a leftoveradmin.confcannot block retries. The service retries after 30 seconds. -
Install a CNI. None is included. Cilium needs
kubeProxyReplacement=trueandk8sServiceHostset to the node address. Until then the node stays NotReady and CoreDNS Pending.
Init pulls images from the network. Offline init is not provided; this lists what would need to be available (including kube-proxy, although it is unused):
kubeadm config images list --config /etc/kubernetes/bluefin/init.yaml
Persistence: installed nodes keep admin.conf and the enable links, skip
init on later boots, and start containerd and kubelet directly. Diskless nodes
lose /etc and /var and create a fresh cluster every boot.
For multi-node homelabs, the
homelab templates configure HOMELAB_ROLE, a unique
hostname, mDNS control-plane endpoint and certificate SANs, and
passphrase-authenticated node joining. The prepare service respects an
already configured endpoint. See the
cluster protocol and threat model.
Host tools
kubeadm v1.35 preflight needs losetup, mount, and cp, supplied by the base
image. conntrack, iptables, ethtool, and socat are not shipped:
conntrack is no longer required, Cilium supplies iptables, kube-proxy is
replaced, and containerd 2 handles port forwarding in-process.
Kernel options
The base kernel supplies Cilium's VXLAN/GENEVE, BPF traffic-control, socket diagnostics, and NOTRACK options. These are base-image modules, signed there, not modules supplied by the kubeadm extension. Exact Kconfig settings and build checks are in the upstream technical reference.
iSCSI
The base image supplies iscsiadm and socket-activated iscsid.
iscsi-init.service creates /etc/iscsi/initiatorname.iscsi only if absent;
provision it through Ignition for a stable identity on diskless nodes, which
otherwise get a new name each boot. Automatic login via iscsi.service is
off. /etc/iscsi/iscsid.conf and /var/lib/iscsi are supplied for host-side
iscsiadm, including democratic-csi's chroot /host use.
NFS
The base image supplies NFS client tools, including mount.nfs, mount.nfs4,
umount.nfs, rpc.statd, nfsidmap, nfsstat, and showmount. There is no
NFS server or GSS/Kerberos support (sec=krb5* is unsupported).
nfs-client.target and rpcbind.socket are enabled; NFSv3 starts
rpc-statd.service on demand for locking. NFSv4 needs neither service and
uses nfsidmap through the kernel's request-key upcall for ID mapping.
For failed PV mounts:
fsconfig() failed: NFS: mount program didn't pass remote addresspoints to a missingmount.nfshelper.Failed to find 'tcp' protocolpoints to missing/etc/protocolsor/etc/services. These are factory links refreshed by A/B updates.- Diskless nodes lose
/var/lib/nfs/statdon reboot, so NFSv3 servers are not notified of locks held before that reboot.
Known gaps
arm64 binaries are pinned, but no aarch64 image has been built or booted.
Verify
On a worker, inspect crictl info and kubeadm init phase preflight.
For a control plane, check that the node and CoreDNS become Ready after CNI
installation. Build and QEMU checks are in the
upstream guide.