Skip to main content

Windows Worker Nodes

Purpose: For platform engineers, shows how to add Windows Server nodes using the opencenter-windows Ansible collection.

Informational Only

Windows worker nodes are not part of the GA support boundary for openCenter 2026.01.0. This page is retained for historical and exploratory use.

Architecture

Windows worker nodes extend an existing openCenter Kubernetes cluster to run Windows container workloads. Key design decisions:

  • Workers only — Kubernetes does not support Windows control plane nodes. The control plane is always Linux.
  • ContainerD runtime — Docker/dockershim was removed in K8s v1.24. ContainerD is the supported runtime.
  • NSSM for kubelet — Windows has no systemd. NSSM (Non-Sucking Service Manager) wraps kubelet as a Windows service.
  • BGP routing — Windows RemoteAccess features installed for Calico pod networking via BGP (idle if using other CNI).
  • SSH connectivity — SSH is the recommended connection method (OpenSSH Server on Windows).
  • Push-based Ansible — Windows node lifecycle differs from Linux; push-based Ansible suits Windows better than GitOps pull.

The collection does NOT handle: CNI plugin installation, kube-proxy config, Windows patching, certificate rotation, or node draining.

Prerequisites

  • A running openCenter Kubernetes cluster with Linux control plane nodes
  • Windows Server 2019 or 2022 VMs with OpenSSH Server enabled
  • Ansible 2.14+ with the ansible.windows collection
  • Administrator credentials on Windows nodes
  • Network connectivity: Windows node → control plane (port 6443), control plane → Windows node (port 10250)
  • oc_controlplane_nodes inventory group defined (for join token generation)

Steps

1. Install the Ansible collection

ansible-galaxy collection install opencenter-cloud.opencenter_windows_workers

Or build from source:

git clone https://github.com/opencenter-cloud/opencenter-windows.git
cd opencenter-windows
ansible-galaxy collection build
ansible-galaxy collection install opencenter-cloud-opencenter_windows_workers-*.tar.gz

2. Prepare the inventory

Create an inventory file listing your Windows nodes. SSH is the recommended connection:

# windows-inventory.ini
[windows_workers]
win-worker-01 ansible_host=192.168.1.30
win-worker-02 ansible_host=192.168.1.31

[windows_workers:vars]
ansible_connection=ssh
ansible_shell_type=powershell
ansible_user=Administrator

[oc_controlplane_nodes]
cp-01 ansible_host=192.168.1.10
cp-02 ansible_host=192.168.1.11
cp-03 ansible_host=192.168.1.12

3. Create the playbook

# windows-workers.yml
---
- name: Setup Windows Kubernetes Workers
hosts: windows_workers
vars:
containerd_version: "1.7.13"
crictl_version: "1.29.0"
kube_version: "1.29.0"
k8s_internal_ip: "192.168.1.20" # Control plane API endpoint or LB VIP
roles:
- opencenter-cloud.opencenter_windows_workers.win-containerd
- opencenter-cloud.opencenter_windows_workers.win-kubeadm

4. Run the playbook

ansible-playbook -i windows-inventory.ini windows-workers.yml

The roles execute in order:

  1. win-containerd — Checks for pending reboots, installs Containers/Hyper-V features, downloads and installs ContainerD, registers it as a Windows service, installs crictl for debugging.
  2. win-kubeadm — Validates ContainerD is running, downloads kubelet/kubeadm, registers kubelet via NSSM, opens firewall port 10250, installs BGP routing features, generates join token from control plane, joins the node.

Expect 10–20 minutes per node. Up to 4 reboots may occur (pending reboot, feature installs, routing features, PATH update).

To rejoin a node after token expiration:

ansible-playbook -i windows-inventory.ini windows-workers.yml --tags join

5. Verify the nodes joined

kubectl get nodes -o wide

Windows nodes appear with OS-Image: Windows Server 2022 and status Ready.

6. Apply a Windows node taint (optional)

To prevent Linux workloads from scheduling on Windows nodes:

kubectl taint nodes win-worker-01 os=windows:NoSchedule
kubectl taint nodes win-worker-02 os=windows:NoSchedule

Windows workloads should include a matching toleration and nodeSelector:

spec:
nodeSelector:
kubernetes.io/os: windows
tolerations:
- key: os
value: windows
effect: NoSchedule

Variables Reference

VariableDefaultRequiredRole
containerd_versionYeswin-containerd
crictl_versionYeswin-containerd
kube_versionYeswin-kubeadm
k8s_internal_ipYeswin-kubeadm
skip_hypervisor_support_checkfalseNowin-containerd
cni_bin_pathc:/opt/cni/binNowin-containerd
cni_config_pathc:/etc/cni/net.dNowin-containerd
containerd_pathC:\Program Files\containerdNowin-containerd
hostname_override{{ ansible_hostname }}Nowin-kubeadm
kubernetes_pathC:\kNowin-kubeadm
kubelet_log_pathC:\var\log\kubeletNowin-kubeadm
nssm_install_directoryC:\Program Files\nssmNowin-kubeadm

Set skip_hypervisor_support_check: true when running on VMs without nested virtualization (uses process-isolated containers instead of Hyper-V isolation).

Verification

# Node status
kubectl get nodes -l kubernetes.io/os=windows

# Run a test Windows pod
kubectl run win-test --image=mcr.microsoft.com/windows/nanoserver:ltsc2022 \
--overrides='{"spec":{"nodeSelector":{"kubernetes.io/os":"windows"}}}' \
--command -- ping -t localhost

kubectl get pod win-test -w
# Should reach Running state

# Cleanup
kubectl delete pod win-test

Troubleshooting

SymptomLikely causeFix
kubeadm join fails with timeoutControl plane IP unreachableVerify: Test-NetConnection -ComputerName <CP_IP> -Port 6443
Node shows NotReadyContainerD service not runningOn Windows: Get-Service containerd, Start-Service containerd
SSH connection refusedOpenSSH Server not installedInstall: Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Calico pods not runningBGP configuration mismatchVerify calico_version matches the Linux cluster's Calico version
Windows pod stuck in ContainerCreatingMissing container base imagePull: crictl pull mcr.microsoft.com/windows/nanoserver:ltsc2022
Kubelet not startingNSSM not registeredRun: nssm install kubelet "C:\k\StartKubelet.ps1"