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:ltsc2022base 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-latestrunner 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.