Windows Worker Nodes
Purpose: For platform engineers, shows how to add Windows Server nodes using the opencenter-windows Ansible collection.
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.windowscollection - Administrator credentials on Windows nodes
- Network connectivity: Windows node → control plane (port 6443), control plane → Windows node (port 10250)
oc_controlplane_nodesinventory 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:
- 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.
- 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
| Variable | Default | Required | Role |
|---|---|---|---|
containerd_version | — | Yes | win-containerd |
crictl_version | — | Yes | win-containerd |
kube_version | — | Yes | win-kubeadm |
k8s_internal_ip | — | Yes | win-kubeadm |
skip_hypervisor_support_check | false | No | win-containerd |
cni_bin_path | c:/opt/cni/bin | No | win-containerd |
cni_config_path | c:/etc/cni/net.d | No | win-containerd |
containerd_path | C:\Program Files\containerd | No | win-containerd |
hostname_override | {{ ansible_hostname }} | No | win-kubeadm |
kubernetes_path | C:\k | No | win-kubeadm |
kubelet_log_path | C:\var\log\kubelet | No | win-kubeadm |
nssm_install_directory | C:\Program Files\nssm | No | win-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
| Symptom | Likely cause | Fix |
|---|---|---|
kubeadm join fails with timeout | Control plane IP unreachable | Verify: Test-NetConnection -ComputerName <CP_IP> -Port 6443 |
Node shows NotReady | ContainerD service not running | On Windows: Get-Service containerd, Start-Service containerd |
| SSH connection refused | OpenSSH Server not installed | Install: Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 |
| Calico pods not running | BGP configuration mismatch | Verify calico_version matches the Linux cluster's Calico version |
Windows pod stuck in ContainerCreating | Missing container base image | Pull: crictl pull mcr.microsoft.com/windows/nanoserver:ltsc2022 |
| Kubelet not starting | NSSM not registered | Run: nssm install kubelet "C:\k\StartKubelet.ps1" |