Skip to content

Instantly share code, notes, and snippets.

@nongvantinh
Last active February 26, 2026 02:49
Show Gist options
  • Select an option

  • Save nongvantinh/97b557df9a25be0dd6e17e124dd8fea8 to your computer and use it in GitHub Desktop.

Select an option

Save nongvantinh/97b557df9a25be0dd6e17e124dd8fea8 to your computer and use it in GitHub Desktop.
Building a Windows Docker image on Windows OS

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.

Preface

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.


1. Installing Windows VM on Ubuntu 22.04 Using QEMU

System Requirements

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

Downloading Windows ISO

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-kvm qemu-system-x86 qemu-utils \
virt-manager 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.

Resizing the Disk

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:

  1. Open Disk Management (diskmgmt.msc).
  2. Locate unallocated space.
  3. Right-click on the C: partition and select Extend Volume.
  4. Follow the wizard to complete the expansion.

2. Setting Up Necessary Tools

To enable clipboard sharing and better display support, install SPICE on Ubuntu:

sudo apt update
sudo apt install spice-vdagent virt-viewer

Start QEMU with SPICE support:

qemu-system-x86_64 \
    -m 16G \
    -smp 10 \
    -drive file=windows_vm.qcow2,format=qcow2 \
    -boot c \
    -enable-kvm \
    -vga qxl \
    -device qxl \
    -device virtio-mouse-pci \
    -spice port=5900,addr=127.0.0.1,disable-ticketing=on \
    -device virtio-serial \
    -chardev spicevmc,id=vdagent,name=vdagent \
    -device virtserialport,chardev=vdagent,name=com.redhat.spice.0 \
    -netdev user,id=net0 \
    -device virtio-net-pci,netdev=net0

To connect to the VM:

remote-viewer spice://127.0.0.1:5900

Start a simple HTTP server on the host so the guest can download files directly from the guest OS. In my case, I know that the interface wlp0s20f3 is my Wi-Fi interface, so on the guest VM I will access 172.0.231.142:8000 to retrieve the files from the host machine.

cd ~/Donwloads
python3 -m http.server 8000

You can use command below to findout the LAN IP address of the host and access from the guest :

ip address

Install SPICE Guest Tools on Window Virtual Machine

Download and install Windows SPICE Guest Tools from:

https://www.spice-space.org/download.html

In the old Windows OS, they don't ship the Edge browser with them and we only have the Internet Browser as default and many website doesn't support it. So, in that case we will need to install The display protocol SPICE + agent via Powershell: Win + X + A:

# Download SPICE Guest Tools installer
$Url = "https://www.spice-space.org/download/windows/spice-guest-tools/spice-guest-tools-latest.exe"
$Out = "$env:TEMP\spice-guest-tools.exe"

Invoke-WebRequest -Uri $Url -OutFile $Out

# Install silently
Start-Process -FilePath $Out -ArgumentList "/S" -Wait

Restart the VM after installation.

These Softwares are for other things I want to test. You can skip it if you don't need them. Install Chrome browser:

# Download Chrome Enterprise installer (64-bit)
$Url = "https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi"
$Out = "$env:TEMP\chrome.msi"

Invoke-WebRequest -Uri $Url -OutFile $Out

# Install silently
Start-Process "msiexec.exe" -ArgumentList "/i `"$Out`" /qn /norestart" -Wait

Install Visual Studio Code:

# Download VS Code (64-bit system installer)
$Url = "https://update.code.visualstudio.com/latest/win32-x64/stable"
$Out = "$env:TEMP\VSCodeSetup.exe"

Invoke-WebRequest -Uri $Url -OutFile $Out

# Silent install
Start-Process -FilePath $Out -ArgumentList "/VERYSILENT /NORESTART /MERGETASKS=!runcode" -Wait

3. Enabling Hyper-V and Installing Docker

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:

Docker Installation Guide

Summary of Docker Installation Steps

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)

4. Test the new windows docker:

Make sure all the step above produce a windows environment:

docker info | Select-String "OSType"

it should print out "windows".

5. Building and Publishing Docker Images

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.


Install Linux docker and Windows docker at the same time

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?

Cleanup Previous Installations

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 and Configure New Docker Versions

Install WSL on Windows to support Linux:

wsl --install

Register Linux Docker

docker context use default
&$Env:ProgramFiles\docker\dockerd --register-service

Register Windows Docker

dockerd.exe -H npipe:////./pipe/docker_windows --service-name docker-win --register-service
docker context create win --docker host=npipe:////./pipe/docker_windows

Start Docker Services

Start-Service docker
Start-Service docker-win

Check the active container type:

docker info | Select-String "OSType"
docker -c win info | Select-String "OSType"

# escape=`
# https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-docker/manage-windows-dockerfile#escape-character
# https://hub.docker.com/r/microsoft/dotnet-framework-sdk
ARG FROM_IMAGE=mcr.microsoft.com/dotnet/framework/sdk:3.5-20241008-windowsservercore-ltsc2022
FROM ${FROM_IMAGE}
SHELL ["cmd", "/S", "/C"]
ADD https://aka.ms/vscollect.exe C:\collect.exe
ADD https://nodejs.org/dist/v16.15.1/node-v16.15.1-x64.msi C:\node-v16.15.1-x64.msi
RUN start /wait msiexec.exe /i C:\node-v16.15.1-x64.msi /l*vx "%TEMP%\MSI-node-install.log" /qn ADDLOCAL=ALL
ADD https://awscli.amazonaws.com/AWSCLIV2.msi C:\AWSCLIV2.msi
RUN msiexec.exe /i C:\AWSCLIV2.msi /quiet /norestart
ADD https://www.python.org/ftp/python/3.10.6/python-3.10.6-amd64.exe C:\python-3.10.6-amd64.exe
RUN start /wait C:\python-3.10.6-amd64.exe /quiet InstallAllUsers=1 PrependPath=1
ADD https://slproweb.com/download/Win64OpenSSL_Light-3_5_0.msi C:\Win64OpenSSL_Light-3_5_0.msi
RUN start /wait msiexec.exe /i C:\Win64OpenSSL_Light-3_5_0.msi /quiet /norestart
RUN powershell -Command "[System.Environment]::SetEnvironmentVariable('PATH', $env:PATH + ';C:\Program Files\OpenSSL-Win64\bin', [System.EnvironmentVariableTarget]::Machine)"
# Download and install Build Tools for Visual Studio 2022.
# https://github.com/microsoft/vs-dockerfiles
ARG CHANNEL_URL=https://aka.ms/vs/17/release/channel
ADD ${CHANNEL_URL} C:\VisualStudio.chman
ADD https://aka.ms/vs/17/release/vs_buildtools.exe C:\vs_buildtools.exe
RUN C:\vs_buildtools.exe --quiet --wait --norestart --nocache `
--channelUri C:\VisualStudio.chman `
--installChannelUri C:\VisualStudio.chman `
--add Microsoft.Component.MSBuild `
--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
--add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
--add Microsoft.VisualStudio.Component.VC.ATL `
--add Microsoft.VisualStudio.Component.VC.ATLMFC `
--add Microsoft.VisualStudio.Component.VC.Redist.14.Latest `
--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core `
--add Microsoft.VisualStudio.Workload.NativeDesktop `
--installPath C:\BuildTools `
&& exit /b 0 `
|| if "%ERRORLEVEL%"=="3010" (exit /b 0) else (C:\collect.exe -zip:C:\vslogs.zip && exit /b %ERRORLEVEL%)
RUN del C:\collect.exe `
&& del C:\node-v16.15.1-x64.msi `
&& del C:\AWSCLIV2.msi `
&& del C:\python-3.10.6-amd64.exe `
&& del C:\Win64OpenSSL_Light-3_5_0.msi `
&& del C:\VisualStudio.chman `
&& del C:\vs_buildtools.exe
CMD ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment