Building Windows Docker Images on Windows
This guide provides instructions on setting up and installing a Windows Server 2022 Virtual Machine on Ubuntu 22.04 to build Docker images for Windows.
Instead of building Windows Docker images on a physical machine, we set up a virtual machine to avoid OS version and kernel mismatches. Initially, I attempted to build a Windows Docker image on my Windows 10 laptop, but encountered errors during the base image pull stage.
Upon investigation, I discovered that my laptop was running Windows 10.0.26100, a Windows Insider Preview build. The error message:
hcsshim::ImportLayer failed in Win32: The system cannot find the path specified. (0x3)
indicated an incompatibility between the Windows version and the container OS version. To resolve this, I opted to use a virtual machine instead.
I used a Dell laptop with the following specifications:
- CPU: Intel(R) Core(TM) Ultra 7 165H
- Architecture: 64-bit
- Cores/Threads: 16 cores, 22 threads
First, download the Windows Server 2022 ISO from a reliable source. I downloaded it here, but I'm not sure if it's a trusted source; however, the ISO works: https://thuegpu.vn/link-download-windows-server/. Then, create a qcow2 file for the virtual machine.
First, install the QEMU program on Ubuntu:
sudo apt update
sudo apt install qemu qemu-kvm qemu-system qemu-utils virt-manager libvirt-bin libvirt-daemon-system libvirt-clients bridge-utils
Then, navigate to the project folder where you preferred to store the files related to the project and create a backing store for the virtual machine. The command below creates a new 100 GB disk image file called windows_vm.qcow2 using the qcow2 format. It will be used as the virtual hard disk for a virtual machine, and the file will grow in size dynamically as data is added to it, up to a maximum of 100 GB. However, later I discovered that 100GB was not enough to build the Windows Docker with the redundant packages I installed in Visual Studio, including C++ and C#. Therefore, I need to resize the partition later.
cd /home/$USER/Projects/qemu/
qemu-img create -f qcow2 windows_vm.qcow2 100G
Now, boot into the Windows installer. Since my Ubuntu device has 32GB of RAM and 16 cores, I allocate half of the RAM and 10 cores for this virtual machine.
qemu-system-x86_64 \
-m 16G \
-smp 10 \
-drive file=windows_vm.qcow2,format=qcow2 \
-cdrom /home/$USER/Projects/qemu/Windows_Server_2022.iso \
-boot d \
-net nic -net user \
-enable-kvm \
-vga std
During installation, choose Windows Server 2022 Standard Evaluation (Desktop Experience). Be sure to manually allocate the partition that will be used to install the OS to prevent issues when resizing the disk later because the unallocated space it's not directly adjacent to the C: partition in Disk Management.
After the installation is complete, shut down the Virtual Machine to boot with another option that persists the state.
Since 100GB was insufficient, I later increased the size to 500GB:
qemu-img resize windows_vm.qcow2 500G
To verify the new size:
qemu-img info windows_vm.qcow2
After booting into Windows, extend the partition:
- Open Disk Management (
diskmgmt.msc
). - Locate unallocated space.
- Right-click on the C: partition and select Extend Volume.
- Follow the wizard to complete the expansion.
To enable clipboard sharing and better display support, install SPICE on Ubuntu:
sudo apt install spice-vdagent virt-viewer spice-client-vdagent
Start QEMU with SPICE support:
qemu-system-x86_64 \
-m 16G \
-smp 10 \
-drive file=windows_vm.qcow2,format=qcow2 \
-boot c \
-net nic -net user \
-enable-kvm \
-vga qxl \
-device virtio-mouse-pci \
-spice port=5900,addr=127.0.0.1,disable-ticketing=on \
-device qxl \
-device virtio-serial \
-chardev spicevmc,id=vdagent,name=vdagent \
-device virtserialport,chardev=vdagent,name=com.redhat.spice.0
To connect to the VM:
remote-viewer spice://127.0.0.1:5900
Download and install Windows SPICE Guest Tools from:
https://www.spice-space.org/download.html
Restart the VM after installation.
Open PowerShell as Administrator and enable Hyper-V:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
Enable-WindowsOptionalFeature -Online -FeatureName Containers -All
Restart Windows and follow the official Docker instructions:
Download Docker binaries:
Expand-Archive C:\Users\Administrator\Downloads\docker-28.0.2.zip -DestinationPath $Env:ProgramFiles
&$Env:ProgramFiles\docker\dockerd --register-service
Start-Service docker
&$Env:ProgramFiles\docker\docker run hello-world:nanoserver
icacls "C:\ProgramData\docker" /grant Everyone:F /T
After the step above, you should have C:\Program Files\docker
. If so, add this path to the system PATH environment variable:
[System.Environment]::SetEnvironmentVariable('PATH', $env:PATH + ';C:\Program Files\docker', [System.EnvironmentVariableTarget]::Machine)
Make sure all the step above produce a windows environment:
docker info | Select-String "OSType"
it should print out "windows".
Log in to the registry:
docker login your-registry.com
Build the image:
cd C:\Users\YourUser\Projects\your-docker-project
docker build -t windows-container .
Tag and push the image:
docker tag windows-container:latest your-registry.com/your-image:tag
docker push your-registry.com/your-image:tag
This completes the setup for building Windows Docker images in a VM on Ubuntu.
These are the documented steps I collected while debugging the reason why my Windows 10 laptop cannot build Windows Docker images. I think this is valuable knowledge that I might need in the future. But why would anyone want to set up both Linux and Windows Docker side by side on a Windows machine? That would cause unnecessary conflicts. Why not just set up another Ubuntu virtual machine to make your life easier?
If needed, remove previous Docker installations:
Stop-Service docker -Force
Stop-Service docker-win -Force
taskkill /F /IM dockerd.exe
sc.exe delete docker
Remove-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\Application\docker" -Force
Remove-Item -Path "C:\ProgramData\docker\volumes" -Force
Install WSL on Windows to support Linux:
wsl --install
docker context use default
&$Env:ProgramFiles\docker\dockerd --register-service
dockerd.exe -H npipe:////./pipe/docker_windows --service-name docker-win --register-service
docker context create win --docker host=npipe:////./pipe/docker_windows
Start-Service docker
Start-Service docker-win
Check the active container type:
docker info | Select-String "OSType"
docker -c win info | Select-String "OSType"