Have you ever needed to build a Windows Docker image but don't have access to a Windows machine? In this guide, I'll show you how to leverage GitHub Actions to build Windows container images without needing a local Windows environment.
Why Build Windows Docker Images?
While Linux containers are more common, Windows containers are essential for:
- .NET Framework applications
- Windows-specific applications
- Legacy Windows applications that need containerization
- Applications that require Windows-specific features
Prerequisites
Before we start, you'll need:
- A GitHub account
- A Docker Hub account
- A repository with your Dockerfile and application code
- Docker Hub credentials configured as GitHub secrets
You can take a look at my example repository for reference.
The Dockerfile
Let's look at a sample Dockerfile that sets up a Python environment on Windows:
# Use a more recent Windows Server Core image
FROM mcr.microsoft.com/windows/servercore:ltsc2022
# Set shell to PowerShell
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# Download and install Python
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \
Invoke-WebRequest -Uri https://www.python.org/ftp/python/3.9.5/python-3.9.5-amd64.exe -OutFile python-3.9.5-amd64.exe ; \
Start-Process python-3.9.5-amd64.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait ; \
Remove-Item python-3.9.5-amd64.exe
# Verify Python installation
RUN python --version
# Set the working directory
WORKDIR /app
# Copy application files
COPY . /app
# Set the startup command
CMD ["python", "app.py"]
This Dockerfile:
- Uses the
mcr.microsoft.com/windows/servercore:ltsc2022
base image - Installs Python 3.9.5
- Sets the working directory to
/app
- Copies the application files to the container
- Specifies the startup command as
python app.py
Setting Up GitHub Actions
Create a .github/workflows/build.yml
file in your repository:
name: Build Windows Docker Image
on:
push:
branches: [ main ]
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
environment: prod
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and Push
shell: powershell
run: |
docker build . -t ${{ secrets.DOCKERHUB_USERNAME }}/windows-python:latest
docker push ${{ secrets.DOCKERHUB_USERNAME }}/windows-python:latest
How It Works
- The workflow runs on
windows-latest
runner provided by GitHub Actions - It checks out your code
- Logs into Docker Hub using your credentials
- Builds the Windows Docker image
- Pushes the image to Docker Hub
- Displays the image digest for verification
Note that in this example, for simplicity, we're using the latest
tag. In a production environment, you should use versioned tags and instead of hardcoding the image name, you can use environment variables.
Important Considerations
- Image Size: Windows containers are typically larger than Linux containers. Plan your registry storage accordingly.
- Build Time: Windows container builds usually take longer than Linux builds.
- Compatibility: Ensure your base image version matches your deployment environment.
Resources
Want to learn more about Docker? Check out these resources:
Conclusion
GitHub Actions makes it possible to build Windows Docker images without a local Windows environment. This approach is particularly useful for cross-platform teams or developers primarily working on non-Windows systems.
If you're new to Docker and want to learn more, I've written a comprehensive free Docker eBook that covers all the basics and more.
Want to try this out but don't have a server? You can use my DigitalOcean referral link to get a free $200 credit!
Have questions or want to share your experience? Feel free to reach out to me on Twitter @bobbyiliev_.
Top comments (1)
I recently installed Docker (since it was a prerequisite for using something) and now I’m ready to go deep. I know its merits but never really prioritized using it before.