Docker has become the de‑facto standard for packaging and running applications, but many Windows‑centric teams wonder how to fit containers into their existing DevOps pipelines. This guide walks you through every stage—installing Docker Desktop, writing a Dockerfile, testing locally, wiring it into CI/CD, and finally deploying at scale—all on a Windows workstation. By the end, you’ll have a repeatable workflow that lets you build, test, and ship code faster while keeping your environment consistent across developers, testers, and production servers.
What You’ll Need
- Windows 10 Pro/Enterprise (1809+) or Windows 11
- Hardware virtualization enabled in BIOS/UEFI
- Docker Desktop for Windows (latest stable release)
- Git and a code editor (VS Code recommended)
- Access to a CI platform (GitHub Actions, Azure Pipelines, or Jenkins)
Step 1: Install Docker Desktop for Windows
First, download Docker Desktop from the official site (docker.com). Run the installer and, when prompted, allow Docker to use the WSL 2 backend. If you don’t already have WSL 2 installed, Docker will guide you through the process:
wsl --install After the reboot, launch Docker Desktop and verify the installation:
docker version You should see both client and server version information. Docker Desktop also adds a tray icon that lets you switch between Linux and Windows containers—a crucial setting for later steps.
Step 2: Choose the Right Container Mode (Linux vs. Windows)
Docker can run two distinct types of containers on Windows:
- Linux containers – run inside a lightweight Linux VM (WSL 2). This is the default and works for most cross‑platform apps.
- Windows containers – run directly on the Windows kernel and are required for .NET Framework or native Windows binaries.
To toggle, right‑click the Docker tray icon and select Switch to Windows containers… (or the opposite). Verify the current mode with:
docker info --format '{{.OSType}}' For a typical DevOps pipeline that builds .NET Core or Node.js apps, stay in Linux mode. If you need to containerise a legacy .NET Framework service, switch to Windows containers before building the image.
Step 3: Create a Dockerfile for Your Application
Navigate to your project root and create a Dockerfile. Below is a sample for a Node.js API that runs on Linux containers:
# Use the official Node LTS image
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Runtime stage
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]
If you are using Windows containers, replace the base image with a Windows variant, e.g., mcr.microsoft.com/windows/servercore:ltsc2022, and adjust the commands accordingly. Remember to keep the Dockerfile declarative—each instruction creates a new layer, which speeds up rebuilds when only a subset of files change.
Step 4: Build and Test the Image Locally
Run the build command from the folder containing the Dockerfile:
docker build -t myapp:dev . Docker will stream each step; watch for any errors. Once built, spin up a container to validate the runtime behavior:
docker run --rm -p 3000:3000 myapp:dev Open http://localhost:3000 in your browser or use curl to hit an endpoint. If you need to debug inside the container, add -it and override the entrypoint:
docker run -it --entrypoint sh myapp:dev When you’re satisfied, tag the image for your registry (e.g., Docker Hub or Azure Container Registry):
docker tag myapp:dev yourrepo.azurecr.io/myapp:1.0.0 Push it with:
docker push yourrepo.azurecr.io/myapp:1.0.0 Make sure you’ve logged in first (docker login) and that the registry permits Windows or Linux images, matching the mode you built in Step 2.
Step 5: Wire Docker into Your CI/CD Pipeline
Most CI platforms already provide Docker agents, but you still need to script the build, test, and push phases. Below is a minimal GitHub Actions workflow (.github/workflows/docker.yml) for a Linux‑container build:
name: Docker CI
on:
push:
branches: [ main ]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Azure Container Registry
uses: azure/docker-login@v1
with:
login-server: yourrepo.azurecr.io
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
- name: Build and push image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: yourrepo.azurecr.io/myapp:${{ github.sha }}
Key points:
- The runner is
windows-latest, which supports both container modes. - We use
docker/setup-buildx-actionto enable multi‑platform builds if needed. - Secrets keep credentials out of the repo.
If you prefer Azure Pipelines or Jenkins, replace the YAML steps with the equivalent docker build and docker push commands inside your pipeline script.
Step 6: Deploy to Production with Docker Compose or Swarm
For a small‑to‑medium service, Docker Compose provides a simple declarative way to orchestrate multiple containers (e.g., API, database, cache). Create a docker-compose.yml in your deployment repo:
version: "3.9"
services:
api:
image: yourrepo.azurecr.io/myapp:${TAG}
ports:
- "80:3000"
environment:
- NODE_ENV=production
depends_on:
- db
db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- SA_PASSWORD=${{ secrets.DB_SA_PASSWORD }}
- ACCEPT_EULA=Y
volumes:
- mssql-data:/var/opt/mssql
volumes:
mssql-data:
Deploy with a single command on your Windows Server (Docker Desktop or Docker Engine installed):
docker compose -f docker-compose.yml up -d If you need higher availability, spin up a Docker Swarm cluster (docker swarm init) and deploy the same compose file as a stack:
docker stack deploy -c docker-compose.yml prod Both approaches let you roll out updates with zero‑downtime using docker service update --image newimage:tag or by recreating the compose stack with a new tag.
Common Mistakes to Avoid
1. Mismatching container mode: Building a Linux image and trying to run it as a Windows container (or vice‑versa) results in cryptic “cannot find the image” errors. Always verify docker info before building.
2. Hard‑coding secrets: Embedding passwords in Dockerfiles or docker‑compose.yml files will expose them in image layers. Use environment variables, Docker secrets, or Azure Key Vault instead.
3. Large base images: Starting from ubuntu:latest adds unnecessary megabytes. Alpine or slim variants keep images lean and speed up pushes.
4. Ignoring .dockerignore: Without a proper .dockerignore, the build context can include node_modules, .git, and other large folders, inflating build time.
5. Forgetting to clean up: Stale images and containers consume disk space. Periodically run docker system prune -f on build agents.
Tips and Tricks
• Multi‑stage builds dramatically reduce final image size—keep build‑time dependencies in a separate stage and copy only the artefacts you need.
• Use BuildKit (enabled by default in Docker Desktop) for parallel layer execution and cache export/import across CI runs.
• Leverage WSL 2 integration for faster Linux‑container file I/O on Windows; avoid the legacy Hyper‑V backend unless you have a specific requirement.
• Tag images with both SHA and semantic version to make rollbacks trivial: myapp:1.2.3 and myapp:${GITHUB_SHA}.
• Enable healthchecks in Dockerfiles so orchestrators can automatically restart unhealthy containers:HEALTHCHECK CMD curl -f http://localhost:3000/health || exit 1
Frequently Asked Questions
Can I run Windows containers on Windows 10 Home?
No. Windows Home lacks the Hyper‑V and container features required for Windows containers. You can still run Linux containers via WSL 2, but for native Windows images you need Windows Pro, Enterprise, or Server editions.
Do I need a separate VM for each container type?
Not for most scenarios. Docker Desktop switches between Linux (WSL 2) and Windows containers on the same host. However, mixing both types in a single compose file isn’t supported; you must run separate stacks for each mode.
How do I debug a failing build in GitHub Actions?
Enable step‑level logging by adding --progress=plain to the build command, and set ACTIONS_RUNNER_DEBUG=true as a secret. The logs will show the exact Docker output, making it easier to pinpoint missing files or permission issues.
Conclusion
Docker containers bring reproducibility, speed, and scalability to Windows‑based DevOps workflows when used correctly. By installing Docker Desktop, selecting the proper container mode, authoring a clean Dockerfile, testing locally, automating builds in CI, and deploying with Compose or Swarm, you create a robust pipeline that can evolve with your team’s needs. Avoid the common pitfalls, apply the tips above, and you’ll find Docker becoming an indispensable part of your Windows development toolkit.
Photo by Claudio Schwarz on Unsplash




