Windows Server can run containers, but the rules are different from the Linux world most Docker documentation assumes. There are two completely separate runtimes with different kernels, two isolation modes with different compatibility guarantees, and a Windows Server host has no Docker Desktop — you install the engine directly. This guide covers what actually works on a Windows Server VPS, how WSL2 fits in, and when Hyper-V isolation is mandatory rather than optional.
Container workloads drive a specific hardware profile: you need nested virtualization enabled and enough RAM to hold both the images and the build cache. If you are provisioning for this, our Windows VPS guide covers what to look for, and our guide to Windows VPS for remote development teams covers the build-agent side.
Two container worlds on one host
| Windows containers | Linux containers (via WSL2) | |
|---|---|---|
| Kernel | Shared Windows kernel (process isolation) or its own kernel in a utility VM (Hyper-V isolation) | Microsoft’s Linux kernel in a lightweight Hyper-V utility VM |
| Base images | mcr.microsoft.com/windows/servercore, nanoserver, dotnet/framework/aspnet | Any Linux image (alpine, debian, ubuntu) |
| Image size | Nano Server ~100–300 MB, Server Core ~1.2–5 GB | Alpine ~5 MB, Debian ~120 MB |
| Docker Desktop | Not supported on Windows Server | Not supported on Windows Server |
| Run with | Docker Engine (native Windows service) or containerd | docker inside a WSL2 distribution, integrated with Windows Docker Engine |
| Host requirement | Containers + Hyper-V features | WSL2 (Server 2022/2025) + nested virtualization |
The key point: Docker Desktop is not licensed or supported on Windows Server. On Server you install the Docker Engine service natively, or run the Linux engine inside a WSL2 distribution. Anything that says “install Docker Desktop” in a Windows Server tutorial is either wrong or is silently installing an unsupported configuration.
Installing the Windows container runtime
# 1. Enable the required features (Hyper-V is needed for Hyper-V isolation and WSL2)
Enable-WindowsOptionalFeature -Online -FeatureName Containers -All -NoRestart
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All -NoRestart
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All -NoRestart
# Reboot
# 2. Install the Docker Engine (moby) via the Microsoft-published package
Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
Install-Package -Name docker -ProviderName DockerMsftProvider -Force
Start-Service docker
# 3. Verify
docker version
docker info --format '{{.OSType}} / {{.Isolation}} / {{.ServerVersion}}'
On a VPS, step 0 is verifying that the host allows nested virtualization. Hyper-V and Hyper-V isolation both need the vmx/svm exposure flag. If Get-ComputerInfo | Select HyperVRequirement* reports a missing hypervisor or the VirtualizationFirmwareEnabled flag is false, your provider has not enabled nested virtualisation and Hyper-V isolation simply will not start. Process isolation still works in that case, which is why it is the fallback for constrained environments.
Process isolation vs Hyper-V isolation
This is the single most misunderstood part of Windows containers.
- Process isolation — containers share the host’s Windows kernel. Fast to start (sub-second), low memory overhead, but the host OS build must match or be newer than the image. You cannot run a
ltsc2019image on a Server 2022 host with process isolation. Mismatched versions fail with0xc0000135or a “no matching image for OS version” error. - Hyper-V isolation — each container gets its own kernel in a minimal utility VM. Starts in a few seconds and costs a few hundred MB of extra RAM, but it lets an older image run on a newer host, and it gives a stronger security boundary for untrusted code.
# Explicit isolation mode
docker run --isolation=process -d --name web1 mcr.microsoft.com/windows/servercore:ltsc2022
docker run --isolation=hyperv -d --name web2 mcr.microsoft.com/windows/servercore:ltsc2019
# Check the host's default and supported isolation
docker info --format '{{json .Isolation}}'
# Match image tag to your host build
[System.Environment]::OSVersion.Version # e.g. 10.0.20348 = Server 2022 -> ltsc2022
A practical rule: use process isolation on a modern single-version fleet for speed and memory efficiency, and use Hyper-V isolation when you need to run legacy base images, when tenants are mutually untrusted, or when a container is exposed to untrusted input.
Running Linux containers with WSL2
WSL2 on Windows Server is available on Server 2022 and Server 2025 (Server 2019 only has WSL1, which is not suitable for Docker). Once installed, you can run the Linux engine inside the distribution and have it act as the Docker context for your Windows shell.
# Install WSL2 with a distribution (Server 2022+ / 2025)
wsl --install -d Ubuntu-22.04
wsl --set-default-version 2
wsl --update
# Inside the distribution, install the Linux engine
wsl -d Ubuntu-22.04 -- bash -lc "curl -fsSL https://get.docker.com | sh && sudo usermod -aG docker \$USER"
# Expose it to Windows Docker clients via a context
docker context create wsl-linux --docker "host=tcp://127.0.0.1:2375"
docker context use wsl-linux
docker run --rm hello-world
Two caveats dominate real deployments. First, WSL2 requires the same nested-virtualization support as Hyper-V isolation, so it will not start on many budget VPS plans. Second, the Linux engine inside WSL2 stores its images in the distribution’s virtual disk, which grows on demand and does not shrink automatically:
# Reclaim space when the WSL2 virtual disk balloons
wsl --shutdown
Optimize-VHD -Path "$env:LOCALAPPDATA\Packages\CanonicalGroupLimited*\LocalState\ext4.vhdx" -Mode Full
Windows container gotchas that cost hours
- Image size. A Server Core base image is over 1 GB, and a full .NET Framework app image can reach 5–8 GB. Plan disk accordingly and prune aggressively with
docker system prune -a. - No
-vhost volume semantics like Linux. Bind mounts work, but the container user’s permissions must match the host ACL, or you get access-denied inside the container. - Layer cache is build-version sensitive. Building a
ltsc2022image on altsc2019host fails at the FROM layer. Pin your base image tags and pin your host build. - Networking. The default NAT network only exposes published ports. For container-to-container traffic on a VPS, use a user-defined network and reference containers by name, exactly as on Linux.
- Server Core as the build base. Use a multi-stage Dockerfile — a
nanoserverruntime stage on top of aservercorebuild stage can cut the final image by an order of magnitude.
# Multi-stage: big build stage, small runtime stage
FROM mcr.microsoft.com/dotnet/sdk:8.0-windowsservercore-ltsc2022 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-ltsc2022
COPY --from=build /app /app
ENTRYPOINT ["dotnet", "C:\\app\\MyApp.dll"]
Choosing the right approach
| Scenario | Recommended approach |
|---|---|
| .NET Framework / full-trust Windows app | Windows container, process isolation, Server Core base |
| Modern .NET 6/8 Linux-targeted service | Linux container on WSL2 (or move to a Linux VPS) |
| Untrusted or third-party code | Windows container with Hyper-V isolation |
| Legacy base image on a new host | Hyper-V isolation |
| VPS without nested virtualisation | Windows containers, process isolation only |
If your stack is mixed, the pragmatic pattern on a Windows VPS is to run Windows containers natively for the .NET Framework services and keep Linux containers in WSL2 for the supporting tooling, rather than trying to force one runtime to do both. Before you commit, verify nested virtualisation support with your provider — that single detail decides whether Hyper-V isolation and WSL2 are available at all, and it is worth confirming in writing before you compare Windows VPS options on price alone.
Related: How to deploy a .NET application on a Windows VPS and IIS application pool configuration for when containers are not the right answer.


