Skip to content

Instantly share code, notes, and snippets.

@YoraiLevi
Created August 5, 2026 10:21
Show Gist options
  • Select an option

  • Save YoraiLevi/1056e68188c921ab5e44b7a29725fb7c to your computer and use it in GitHub Desktop.

Select an option

Save YoraiLevi/1056e68188c921ab5e44b7a29725fb7c to your computer and use it in GitHub Desktop.
Windows containers with Docker Desktop — isolation, users, privileges, PowerShell, mounts, and Windows-specific flags

Windows containers with Docker Desktop: a short practical guide

Docker commands look almost identical for Linux and Windows containers, but the runtime model is different. A container image must match the active engine:

Linux image   → Linux kernel   → Docker Linux engine (usually WSL2 on Windows)
Windows image → Windows kernel → Docker Windows engine

Docker Desktop can switch between its Linux and Windows engines, but it does not run both engines simultaneously. If you need both at once, one practical development setup is Docker Desktop for Windows containers plus a separate Podman machine or Docker Engine in WSL for Linux containers.

Sources:

Select and verify the Windows engine

docker desktop engine ls
docker desktop engine use windows
docker info --format 'Server OS={{.OSType}} Architecture={{.Architecture}}'

Expected:

Server OS=windows Architecture=x86_64

Switch back to Docker's Linux engine later with:

docker desktop engine use linux

Hello world

Windows container through Docker Desktop:

docker run --rm --isolation=hyperv `
  mcr.microsoft.com/windows/nanoserver:ltsc2022 `
  cmd.exe /C "echo Hello from Windows!"

Linux container through a separate Podman engine:

podman run --rm docker.io/library/alpine echo "Hello from Linux!"

--rm deletes the stopped container automatically. The downloaded image stays cached for later runs.

PowerShell inside a Windows container

Yes, Windows containers can run both Windows PowerShell 5.1 (powershell.exe) and modern PowerShell 7 (pwsh). The executable available depends on the image.

Windows PowerShell 5.1

Windows Server Core includes powershell.exe:

docker run --rm -it --isolation=hyperv `
  mcr.microsoft.com/windows/servercore:ltsc2022 `
  powershell.exe -NoLogo -NoProfile

Inside the container:

$PSVersionTable
Write-Output 'Hello from Windows PowerShell'
exit

For a non-interactive command:

docker run --rm --isolation=hyperv `
  mcr.microsoft.com/windows/servercore:ltsc2022 `
  powershell.exe -NoLogo -NoProfile `
  -Command "Write-Output 'Hello from Windows PowerShell'; Get-Host | Select-Object Version"

PowerShell 7 (pwsh)

Microsoft's maintained .NET SDK images include PowerShell 7. Use an explicit Windows Server Core tag while Docker is running its Windows engine:

docker run --rm -it --isolation=hyperv `
  mcr.microsoft.com/dotnet/sdk:8.0-windowsservercore-ltsc2022 `
  pwsh -NoLogo

Inside the container:

$PSVersionTable
Write-Output 'Hello from PowerShell 7'
exit

Or run one command without opening an interactive shell:

docker run --rm --isolation=hyperv `
  mcr.microsoft.com/dotnet/sdk:8.0-windowsservercore-ltsc2022 `
  pwsh -NoLogo `
  -Command "Write-Output 'Hello from pwsh'; Get-Host | Select-Object Version"

Why it did not work in Nano Server

The plain Nano Server image used in the earlier hello-world example does not include PowerShell, WMI, or the Windows servicing stack. Its smaller footprint is intentional.

Choose the image according to the workload:

Requirement Suggested image
Small modern application with no shell dependency Nano Server
Built-in Windows PowerShell 5.1 or traditional Windows APIs Server Core
PowerShell 7 plus the .NET SDK .NET SDK Windows Server Core image

Server Core and .NET SDK images are substantially larger than Nano Server. For production, build a purpose-specific image containing only the runtime and modules the application requires.

References:

Isolation: process versus Hyper-V

Windows containers support two isolation implementations:

Mode Kernel Strengths Trade-offs
process Shared with the Windows host Fast startup and lower overhead Host/image kernel compatibility matters; weaker boundary
hyperv A lightweight private kernel per container Stronger boundary and broader image compatibility More startup time and memory
default Host policy Convenient Behavior changes by host type

Windows 10/11 normally defaults to Hyper-V isolation. Windows Server normally defaults to process isolation.

docker run --rm --isolation=hyperv IMAGE COMMAND
docker run --rm --isolation=process IMAGE COMMAND
docker run --rm --isolation=default IMAGE COMMAND

Use Hyper-V isolation by default on a development workstation:

docker run --rm --isolation=hyperv `
  mcr.microsoft.com/windows/nanoserver:ltsc2022 cmd.exe /C ver

The version printed by ver is the container image's Windows kernel version. With Hyper-V isolation, that kernel can differ from the Windows 11 host.

Use process isolation only when:

  • the host and image versions are compatible;
  • lower overhead is important; or
  • a supported host-device class must be shared with the container.

Process isolation on Windows 10/11 is intended for development and testing. Use Windows Server and an appropriate support model for production workloads.

Users and privileges

Windows containers do not use Linux UID/GID permissions or Linux capabilities. They use Windows accounts, access-control lists, tokens, and service identities.

Common built-in identities are:

  • ContainerUser: restricted; preferred for normal application execution.
  • ContainerAdministrator: administrator inside the container; useful while installing or configuring software, but avoid it for routine execution.

Select the runtime identity with --user:

docker run --rm --user ContainerUser `
  mcr.microsoft.com/windows/nanoserver:ltsc2022 whoami

Or make it part of the image:

FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
COPY app.exe C:\app\app.exe
USER ContainerUser
ENTRYPOINT ["C:\\app\\app.exe"]

Nano Server commonly defaults to ContainerUser; Server Core commonly defaults to ContainerAdministrator. Check the image instead of assuming:

docker run --rm IMAGE whoami

Host-side privilege warning

The Windows Docker engine is a privileged host service. A trusted user who can fully control that engine can generally obtain administrator-equivalent control of the host. Membership in the local docker-users group should therefore be treated as privileged access.

Hyper-V isolation strengthens the boundary between the workload and host, but it does not make unsafe mounts, credentials, or Docker daemon access harmless.

Domain identity with gMSA

Windows containers can use a Group Managed Service Account through a credential specification:

docker run --security-opt "credentialspec=file://webapp.json" IMAGE

This is an Active Directory deployment feature, not a substitute for ContainerUser in a simple local container.

Useful Windows docker run flags

Lifecycle

--name NAME       stable container name
--rm              remove the container when it exits
-d                run in the background
-it               interactive terminal
--restart POLICY  restart behavior for a long-running service

Isolation and identity

--isolation=hyperv|process|default
--user ContainerUser|ContainerAdministrator
--security-opt credentialspec=file://SPEC.json

Resources

--memory 1GB
--cpus 2
--cpu-percent 50
--cpu-shares 7500
--storage-opt size=30GB
--io-maxbandwidth BYTES_PER_SECOND
--io-maxiops OPERATIONS_PER_SECOND

Example:

docker run --rm --isolation=hyperv --memory 1GB --cpus 2 `
  mcr.microsoft.com/windows/nanoserver:ltsc2022 cmd.exe /C ver

Environment and metadata

-e NAME=VALUE
--env-file PATH
--hostname NAME
--label KEY=VALUE

Windows images do not automatically inherit arbitrary host environment variables. Pass required values explicitly, and do not put secrets directly in commands that may be saved in shell history.

Ports and networking

-p HOST_PORT:CONTAINER_PORT
--network NETWORK
--dns ADDRESS
--add-host NAME:ADDRESS

Example with IIS:

docker run -d --name windows-iis --isolation=hyperv -p 8080:80 `
  mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

Open http://localhost:8080. The IIS/Server Core image is much larger than Nano Server.

Windows bind mounts

Windows paths are used for both source and destination:

New-Item C:\ContainerData -ItemType Directory -Force

docker run --rm --isolation=hyperv `
  --mount 'type=bind,source=C:\ContainerData,target=C:\data' `
  mcr.microsoft.com/windows/nanoserver:ltsc2022 `
  cmd.exe /C "echo persistent data>C:\data\hello.txt"

Get-Content C:\ContainerData\hello.txt

For a read-only mount:

-v C:\ContainerData:C:\data:ro

Windows ACLs still matter. Do not mount sensitive locations such as the entire host C:\ drive into an untrusted container.

Host devices

Supported Windows device classes can be exposed only to process-isolated containers:

docker run --isolation=process `
  --device=class/DEVICE-INTERFACE-CLASS-GUID IMAGE

Hyper-V-isolated Windows containers do not support this device-sharing model.

Linux concepts that do not translate directly

These Linux-oriented options depend on Linux kernel facilities and generally do not apply to Windows containers:

--privileged
--cap-add / --cap-drop
--pid=host
--tmpfs
--device=/dev/...
Linux UID/GID mappings
SELinux/AppArmor labels
seccomp profiles

Windows uses Hyper-V or process isolation, Windows access tokens, ACLs, job objects, and device-interface classes instead.

Other visible differences include:

Linux container Windows container
Usually sh or bash Usually cmd.exe or PowerShell when included
Paths such as /app/data Paths such as C:\app\data
UID/GID and capabilities Windows users, groups, tokens, and ACLs
Linux base images Nano Server, Server Core, or Windows images
Generally small base images Windows base images are usually much larger

Inspect and troubleshoot

Show the active engine:

docker info --format '{{.OSType}}'

Inspect a running container's isolation and user configuration:

docker inspect --format `
  'Isolation={{.HostConfig.Isolation}} User={{.Config.User}}' CONTAINER

List Windows containers by isolation mode:

docker ps --filter isolation=hyperv
docker ps --filter isolation=process

Show every flag supported by the installed CLI:

docker run --help

Common failures:

Error Likely cause
no matching manifest for linux/amd64 Linux engine selected for a Windows image
image operating system "windows" cannot be used Wrong engine selected
OS version incompatibility Use Hyper-V isolation or a compatible image tag
Access denied on a bind mount Host/container ACL mismatch
--device rejected Device sharing requires process isolation

Recommended defaults

For local Windows-container development:

  1. Prefer --isolation=hyperv.
  2. Run the application as ContainerUser where possible.
  3. Give the container explicit CPU and memory limits when it is long-running.
  4. Mount only the exact host directories required.
  5. Treat docker-users membership and Docker daemon access as privileged.
  6. Rebuild from Microsoft’s updated base images instead of patching a running Windows container like a normal VM.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment