Our Windows 11 desktop runs in a container on a rented Linux server. Docker starts it today, and three Podman versions started the same container without a change to its flags. The next question comes from anyone who runs Kubernetes at work: why not schedule it there, next to everything else?
The honest answer starts with a vocabulary problem. "Windows in a container" names three different things, and only two of them give you a full Windows desktop. This post separates them, then covers what Kubernetes adds over Docker and Podman for the one that matters here, what it costs a server, and where pasta, the user-mode network tool now under Podman, QEMU, libvirt and KubeVirt, fits in.
We did not run Kubernetes on our server for this article. The Docker and Podman results are our own. The Kubernetes and KubeVirt sections rest on their documentation and source code, quoted below. The warning section explains why we kept a kubelet off a server that someone works on.
Three things called "Windows in a container"
Scroll to compare columns and rows when the table extends beyond the box.
| Kind | Windows 11 desktop? | Runs on |
|---|---|---|
| Windows containers [1] | No | Windows Server |
| Windows VM in a Linux container [2] | Yes | Linux with KVM |
| KubeVirt virtual machine [3] | Yes | Kubernetes with KVM |
- Windows containers: Windows Server user space sharing the host's Windows kernel. Typical use: IIS or a .NET Framework app. Run by a container engine on Windows Server, or by Kubernetes with Windows nodes.
- Windows VM in a Linux container: a full Windows install under QEMU and KVM, packaged as a container image such as dockur/windows. Docker, Podman or Kubernetes can run it.
- KubeVirt virtual machine: a full Windows install under QEMU and KVM, run in a pod and managed as a Kubernetes
VirtualMachineobject. Needs Kubernetes plus the KubeVirt add-on.
Windows containers are not a Windows desktop. Kubernetes has supported Windows worker nodes since version 1.14 in 2019. Its Windows guide sets the limits:
- "you can only run the control plane on Linux".
- Windows nodes must run Windows Server 2022 or Windows Server 2025.
- "Kubernetes does not support running Windows containers with Hyper-V isolation."
- Privileged containers are "not supported for Windows containers"; HostProcess containers are the substitute.
The images come from Microsoft. Its fullest base image, called simply "Windows", "contains the full set of Windows APIs and system services (minus server roles)", and "The Windows image isn't available for Windows Server 2025." The host and image must also agree on version. Microsoft's compatibility page says Windows Server containers "are blocked from starting when the build number between the container host and the container image are different", with one exception for Windows Server 2022 images on Windows 11.
We found no Microsoft page that says "no desktop" in so many words. In practice, a Windows container is a server process, not a session you sign in to over Remote Desktop. Use Windows containers for Windows server software; they are not a way to host Windows 11.
The other two columns both run a complete Windows install as a virtual machine. They differ in who owns the virtual machine: a container image you bring (dockur), or Kubernetes itself (KubeVirt).
What a Windows VM container needs from any engine
dockur's requirement is "Docker or Podman on a Linux host with KVM support". Under any engine, the container needs the same five things:
- The KVM device (
/dev/kvm), so Windows runs at hardware speed. - A TUN device (
/dev/net/tun) and theNET_ADMINcapability, so the container can build its own network for the VM. - A persistent disk.
- About two minutes to shut down, so Windows closes cleanly.
- Ports kept private.
Docker and Podman grant each of those with one flag: --device /dev/kvm, --device /dev/net/tun, --cap-add NET_ADMIN, a volume and --stop-timeout 120.
Kubernetes has no --device flag in a pod spec. dockur publishes a Kubernetes manifest, installed with one kubectl apply. It gets the devices the blunt way: the container is privileged: true, adds NET_ADMIN, and mounts both devices from the node as hostPath volumes of type CharDevice. The rest is sensible:
- Disk: a 64 Gi
ReadWriteOnceclaim. - Restarts: a single-replica Deployment with the
Recreatestrategy, so two copies never hold the disk at once. - Shutdown:
terminationGracePeriodSeconds: 120, matching the two minutes Windows needs. - Network: a
ClusterIPService, so nothing is published outside the cluster.
That pod fails every relevant check in Kubernetes' Pod Security Standards. The "baseline" profile, the middle of the three, says:
- "Privileged Pods disable most security mechanisms and must be disallowed."
- "HostPath volumes must be forbidden."
- "Adding additional capabilities beyond those listed below must be disallowed", and
NET_ADMINis not on that list.
To run it, the namespace has to allow the "privileged" profile. On a cluster that other people use, that is a decision for whoever owns the cluster, not a detail of the deployment.
Kubernetes does have a cleaner mechanism for hardware. The device plugin framework, stable since 1.26, lets a node "advertise system hardware resources to the Kubelet", the way GPUs appear as nvidia.com/gpu. We found no general-purpose KVM device plugin in Kubernetes' own documentation. KubeVirt brings one.
Two cheap fixes apply to the manifest as published:
- Pin the image. It names
dockurr/windowswith no tag, so a restart can pull a different release onto a disk installed by an older one. Pin it by digest. - Keep the Service internal.
ClusterIPis the right default; reach the desktop through your own gateway rather than aLoadBalancer.
KubeVirt: virtual machines as Kubernetes objects
KubeVirt adds virtual machines to the Kubernetes API. It is a CNCF project, accepted on September 6, 2019 and incubating since April 19, 2022. Its latest release is v1.9.1, from October 12, 2026. Each running VM lives in its own virt-launcher pod. A node agent, virt-handler, publishes KVM as a schedulable resource named devices.kubevirt.io/kvm; KubeVirt's metrics count "nodes in the cluster that have the devices.kubevirt.io/kvm resource available." That is the device-plugin route that dockur's manifest replaces with privileged: true.
The installation guide says "Hardware with virtualization support is recommended". Without it, a useEmulation setting falls back to software emulation, which is far too slow for a desktop.
Windows 11 has specific needs, and KubeVirt documents them:
- A TPM. "The Microsoft Windows 11 installer requires the presence of a TPM device, even though it doesn't use this." A VM gets one with
tpm: {}, and it is "fully emulated (vTPM)", so the server needs no TPM chip of its own. - EFI and Secure Boot. "Enabling EFI automatically enables Secure Boot" unless you turn it off, and Secure Boot needs the SMM CPU feature.
- Drivers. The VirtIO drivers for Windows ship as a container disk, and "Some drivers are required in the installation phase."
- Install media. The Containerized Data Importer loads an ISO or disk image into a persistent volume claim.
- Moving a running VM. Live migration needs storage with "shared ReadWriteMany (RWX) access mode". A single server's local disk is not that.
KubeVirt earns its extra components on a real cluster: several nodes, shared storage, and VMs that must move during maintenance. On one server, most of that has nothing to act on.
Networking, and where pasta fits
Every one of these setups stacks two network layers. The engine connects the container or pod. Then dockur builds a second network inside it for Windows. dockur "Supports NAT, user-mode, macvlan, and macvtap networking", and NAT is the default. A Windows VM in a Kubernetes pod therefore sits behind dockur's NAT, then the pod network, then a Service. That is fine for a desktop that mostly reaches out and receives Remote Desktop on one port.
The engine layer differs:
- Docker uses a bridge and NAT built with iptables, with nftables support. Docker calls its default bridge "not recommended for production use"; a named network is the fix.
- Podman uses Netavark when run as root and pasta when rootless. Pasta has been the rootless default since Podman 5.0, and Podman 6.0 removed the older slirp4netns (release notes).
- Kubernetes hands the pod network to a CNI plugin, and Services give stable addresses. k3s, a small Kubernetes distribution, bundles Flannel for this.
pasta and passt are the same program. The project is "Plug A Simple Socket Transport" (passt) and "Pack A Subtle Tap Abstraction" (pasta):
- passt gives a virtual machine a network made from the host's ordinary sockets.
- pasta does the same for a network namespace, which is what a container is.
- Privileges: neither needs "any capabilities or privileges".
- No NAT by default: pasta copies the host's addresses into the namespace, "avoiding any need for NAT".
- A drop-in successor: the project describes it as "bug-to-bug compatible" with slirp4netns, the tool it replaces.
That makes it the common thread across all four stacks:
Scroll to compare columns and rows when the table extends beyond the box.
| Stack | Role | Since |
|---|---|---|
| Podman | Rootless default | 5.0 |
| Docker | Rootless, experimental | Engine 25.0 |
| QEMU | VM network | 7.2; native in 10.1 |
| libvirt | Interface backend | 9.0.0 |
| KubeVirt | passtBinding (beta) |
v1.8.0 |
The details behind each row:
- Podman: pasta became the default for rootless containers in Podman 5.0, in March 2024.
- QEMU:
-netdev streamwith a separate passt process since QEMU 7.2; a native-netdev passtbackend since QEMU 10.1. - libvirt:
<backend type='passt'/>on a user-mode interface, since libvirt 9.0.0. - KubeVirt: the
passtBindingnetwork binding, beta since v1.8.0.
For Docker, its rootless documentation says "The pasta network driver is experimental, but provides improved throughput". You select it with DOCKERD_ROOTLESS_ROOTLESSKIT_NET=pasta. QEMU's own documentation says "passt can be used as a simple replacement for SLIRP (-net user). passt doesn't require any capability or privilege."
KubeVirt's passt binding needs the PasstBinding feature gate, "adds 250Mi of memory to the VM's resource requirements", and "automatically applies Network Address Translation (NAT)" for the guest.
What that means for a Windows VM. If you want no root anywhere, passt and pasta are how you get networking: rootless Podman, QEMU or libvirt run by a normal user, or KubeVirt's binding. If you run the engine as root on a server you control, as we do, you do not need them, and dockur's own NAT does the job. qemus/qemu, the base project dockur builds on, notes the cost of the user-mode route: with "user-mode networking (for example when running under Podman)", every extra port has to be listed in USER_PORTS.
Warning: a kubelet changes host-wide kernel settings
Read this before you run Kubernetes on a machine that does anything else. The kubelet, the Kubernetes agent on every node, tunes the kernel when it starts. Its configuration reference describes
protectKernelDefaults: "if true, causes the Kubelet to error if kernel flags are not as it expects. Otherwise the Kubelet will attempt to modify kernel flags to match its expectation. Default: false."The values it sets are in its source code, with the numbers in Kubernetes' sysctl helper:
vm.overcommit_memory = 1vm.panic_on_oom = 0kernel.panic = 10kernel.panic_on_oops = 1- two kernel key limits
Those settings belong to the whole machine, not to a container. A kubelet inside a privileged container, which is how k3s in Docker, kind and k3d run, changes them for the host.
Before writing this, we checked our server:
vm.overcommit_memory,kernel.panicandkernel.panic_on_oopswere all 0. A Kubernetes test would have switched them to 1, 10 and 1 for as long as it ran. Withpanic_on_oops=1andpanic=10, any kernel oops reboots the server ten seconds later, along with the Windows desktop someone is working in.Those values are sensible on a dedicated Kubernetes node, where failing fast and rebooting is the point. On a shared workstation they are a surprise. Give Kubernetes its own machine or virtual machine. If you must share, set the values yourself on purpose and turn on
protectKernelDefaults, so the kubelet refuses to start rather than silently changing them.
What Kubernetes buys on one server, and what it costs
On a single server, Kubernetes gives you:
- declarative files that also work on fifty nodes;
- controllers that restart a failed pod;
- persistent volume claims and Services with stable names;
- one way of working if you already run a cluster.
It costs:
- Memory and CPU for the control plane. k3s lists 2 cores and 2 GB for a server node.
- A privileged pod or KubeVirt anyway. Neither escapes the need for KVM access.
- The kernel settings above.
- A third network layer.
- A version treadmill. The project supports the three newest minor releases with about a year of patches each. Kubernetes 1.37 is current, and k3s v1.37.1 shipped on September 30, 2026.
What it does not give you on one server is failover. A ReadWriteOnce disk on local storage cannot follow the pod anywhere.
Docker Compose and Podman's Quadlet units cover the same single-server needs with less underneath:
- a restart policy or a systemd service;
- a named volume or a bind mount;
- ports bound to
127.0.0.1; - the devices granted by name instead of a privileged container.
Licensing on hosted servers
dockur is direct about whose problem this is: "You are responsible for ensuring that you have a valid Windows license and that your use complies with Microsoft's licensing terms." Four Microsoft rules are worth knowing before you choose where Windows runs:
- Dedicated hosts from the big four clouds. Since October 1, 2019, Microsoft's terms say licenses "purchased without Software Assurance and mobility rights cannot be deployed" on dedicated hosted cloud services from its Listed Providers: "Microsoft, Alibaba, Amazon (including VMware Cloud on AWS), and Google." If your dedicated server comes from one of them, read that page before bringing your own Windows license.
- Multi-session Windows. "It's against the licensing agreement to run Windows multi-session outside of the Azure Virtual Desktop service for production purposes."
- Windows containers. Microsoft's container FAQ ties the image license to "a validly licensed host OS". Windows Server Standard allows unlimited process-isolated containers and two Hyper-V-isolated ones. Running Windows containers on Windows 11 Pro or Enterprise is "for test or development purposes only".
- Everything else depends on your edition and agreement. Microsoft's Product Terms are the reference, not a blog post, including this one.
How to choose
Scroll to compare columns and rows when the table extends beyond the box.
| You want | Use |
|---|---|
| One desktop, one server [1] | Docker or Podman |
| Windows server apps at scale [2] | Kubernetes, Windows nodes |
| Many VMs across nodes [3] | KubeVirt |
| One desktop on your cluster [4] | dockur's manifest |
| No root anywhere [5] | Podman rootless, or passt |
| A shared workstation [6] | Not Kubernetes |
- One desktop, one server: Docker or Podman, run as root, with dockur. The devices are granted by name, with the least machinery.
- Windows server apps at scale: IIS or .NET Framework software belongs in Windows containers on Kubernetes Windows Server nodes, with a Linux control plane.
- Many VMs across nodes: KubeVirt on a cluster with shared storage gives you VM objects, a KVM device plugin and live migration.
- One desktop on your cluster: dockur's manifest in a namespace allowed the privileged profile, with the image pinned. It works with what you have, at a known security cost.
- No root anywhere: rootless Podman with pasta, or QEMU or libvirt with passt. User-mode networking needs no privileges, but we have not proven rootless Windows.
- A shared workstation: not Kubernetes, because of the kubelet's kernel settings.
For our server the answer stays Docker, with Podman as the tested alternative. It is one machine, one Windows desktop and one owner. Kubernetes would add a control plane, a privileged pod and host-wide kernel changes, and it would give no failover in return.
What this article is based on
- Docker: the Windows 11 desktop in production on our server since October 14, 2026, described in Windows 11 in a Browser on Your Own Linux Server.
- Podman: the same container started under Podman 5.7.0, 5.8.7 and a 6.2 development build on October 14, 2026, described in Podman or Docker?.
- Kubernetes and KubeVirt: documentation and source code only. We did not run a kubelet on our production server, for the reasons in the warning above. The kernel values quoted there were read from that server, not changed on it.
Fact-check notes and sources
- Windows containers: Kubernetes Windows guide, Kubernetes 1.14 Windows announcement, Microsoft containers overview, base images and version compatibility.
- dockur on Kubernetes: kubernetes.yml and the README (requirements, networking modes, licensing), read October 14, 2026. qemus/qemu for
USER_PORTS. - Kubernetes security and devices: Pod Security Standards, device plugins and volumes (
CharDevice). - The kubelet's kernel settings: kubelet configuration reference, container_manager_linux.go and sysctl.go. Host values read on our server on October 14, 2026.
- KubeVirt: project site, CNCF project page, releases, metrics, and the user guide on installation, TPM and UEFI state, virtual hardware, Windows VirtIO drivers, CDI, live migration and interfaces and networks.
- passt and pasta: passt.top, QEMU network devices, QEMU QAPI schema (
-netdev passt"Since: 10.1"), libvirt domain format, Podman release notes and Docker rootless troubleshooting. - Docker networking: bridge networks and packet filtering and firewalls.
- k3s and releases: k3s documentation, k3s requirements, k3s releases and Kubernetes releases.
- Licensing: Microsoft dedicated-cloud licensing change, Windows multi-session FAQ and Windows containers FAQ.
Related reading
- Podman or Docker? We Started the Same Windows 11 VM Under Both covers the daemon, Quadlet services, secrets and Compose.
- Windows 11 in a Browser on Your Own Linux Server covers the running Docker setup, Guacamole, Cloudflare Access and Drive backups.
- The Sovereign AI Playbook introduces the server and its software map.
- Sovereign AI Made Simple summarizes the series and its downloads.
This post is informational, not legal, licensing or security-consulting advice. Check Microsoft's Product Terms and your own agreements before running Windows on any hosted server. Mentions of Kubernetes, KubeVirt, k3s, Docker, Podman, passt, QEMU, libvirt, Microsoft, Amazon, Google, Alibaba, VMware and dockur are nominative fair use. No affiliation is implied.