Windows Server Containers and Docker: Modern Application Deployment
Introduction
Windows Server containers enable lightweight, portable application deployment. This guide covers Windows container fundamentals, Docker Desktop installation, creating container images, Kubernetes orchestration on Windows, container networking, persistent storage, and hybrid Windows/Linux workloads.
Windows Container Fundamentals
Container Types
Windows Server Containers (Process Isolation):
- Shared kernel with host OS
- Lightweight, fast startup
- Requires matching OS version
Hyper-V Containers (Hypervisor Isolation):
- Dedicated kernel per container
- Stronger isolation
- Supports mixed OS versions
Prerequisites
# Check Windows version
Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsHardwareAbstractionLayer
# Windows Server 2019+ or Windows 10/11 Pro/Enterprise required
# Enable Containers feature
Install-WindowsFeature -Name Containers -Restart
# Enable Hyper-V (for Hyper-V containers)
Install-WindowsFeature -Name Hyper-V -IncludeManagementTools -Restart
Installing Docker on Windows Server
Docker Engine Installation
# Install Docker EE (Enterprise Edition) for Windows Server
Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
Install-Package -Name docker -ProviderName DockerMsftProvider -Force
# Start Docker service
Start-Service Docker
# Configure Docker to start automatically
Set-Service Docker -StartupType Automatic
# Verify installation
docker version
docker info
# Test with hello-world
docker run hello-world:nanoserver
Docker Desktop (Windows 10/11)
# Download Docker Desktop from https://www.docker.com/products/docker-desktop/
# Run installer: Docker Desktop Installer.exe
# Enable Windows containers mode
# Right-click Docker icon → "Switch to Windows containers..."
# Configure resources
# Settings → Resources → WSL Integration (for Linux containers)
# Settings → Resources → Advanced (CPU, Memory allocation)
Docker Configuration
# Configure Docker daemon (C:\ProgramData\docker\config\daemon.json)
$daemonConfig = @{
"insecure-registries" = @("registry.contoso.com:5000")
"registry-mirrors" = @("https://mirror.gcr.io")
"log-driver" = "json-file"
"log-opts" = @{
"max-size" = "10m"
"max-file" = "3"
}
"storage-opts" = @("size=120GB")
} | ConvertTo-Json
$daemonConfig | Out-File C:\ProgramData\docker\config\daemon.json -Encoding ASCII
# Restart Docker
Restart-Service Docker
Building Windows Container Images
Dockerfile for .NET Application
# Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0-nanoserver-ltsc2022
# Set working directory
WORKDIR /app
# Copy application files
COPY bin/Release/net6.0/publish/ .
# Expose port
EXPOSE 80
# Set entry point
ENTRYPOINT ["dotnet", "MyWebApp.dll"]
Building and Running Containers
# Build image
docker build -t contoso/mywebapp:1.0 .
# View images
docker images
# Run container
docker run -d `
--name mywebapp `
-p 8080:80 `
contoso/mywebapp:1.0
# View running containers
docker ps
# View all containers (including stopped)
docker ps -a
# View container logs
docker logs mywebapp
# Follow logs
docker logs -f mywebapp
# Execute command in container
docker exec -it mywebapp powershell
# Stop container
docker stop mywebapp
# Remove container
docker rm mywebapp
# Remove image
docker rmi contoso/mywebapp:1.0
Multi-Stage Build Example
# Multi-stage Dockerfile
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:6.0-nanoserver-ltsc2022 AS build
WORKDIR /src
# Copy project files
COPY ["MyWebApp/MyWebApp.csproj", "MyWebApp/"]
RUN dotnet restore "MyWebApp/MyWebApp.csproj"
# Copy source and build
COPY . .
WORKDIR "/src/MyWebApp"
RUN dotnet build "MyWebApp.csproj" -c Release -o /app/build
# Stage 2: Publish
FROM build AS publish
RUN dotnet publish "MyWebApp.csproj" -c Release -o /app/publish
# Stage 3: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:6.0-nanoserver-ltsc2022 AS final
WORKDIR /app
COPY --from=publish /app/publish .
EXPOSE 80
ENTRYPOINT ["dotnet", "MyWebApp.dll"]
IIS Container Example
# IIS with ASP.NET
FROM mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022
# Install ASP.NET
RUN powershell -Command `
Add-WindowsFeature Web-Asp-Net45
# Copy website
COPY website/ C:/inetpub/wwwroot/
# Expose port
EXPOSE 80
# Start IIS
ENTRYPOINT ["C:\\ServiceMonitor.exe", "w3svc"]
Container Orchestration with Kubernetes
Installing Kubernetes on Windows
# Enable Kubernetes in Docker Desktop
# Docker Desktop → Settings → Kubernetes → Enable Kubernetes
# Or install minikube for testing
choco install minikube
# Start minikube with Windows containers
minikube start --driver=hyperv --container-runtime=docker
# Install kubectl
choco install kubernetes-cli
# Verify installation
kubectl version
kubectl cluster-info
Deploying Windows Containers to Kubernetes
deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mywebapp
labels:
app: mywebapp
spec:
replicas: 3
selector:
matchLabels:
app: mywebapp
template:
metadata:
labels:
app: mywebapp
spec:
nodeSelector:
kubernetes.io/os: windows
containers:
- name: mywebapp
image: contoso/mywebapp:1.0
ports:
- containerPort: 80
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1000m"
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
service.yaml:
apiVersion: v1
kind: Service
metadata:
name: mywebapp-service
spec:
type: LoadBalancer
selector:
app: mywebapp
ports:
- protocol: TCP
port: 80
targetPort: 80
Deploying Application
# Apply deployment
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# View deployments
kubectl get deployments
# View pods
kubectl get pods
# View services
kubectl get services
# Scale deployment
kubectl scale deployment mywebapp --replicas=5
# View pod details
kubectl describe pod <pod-name>
# View logs
kubectl logs <pod-name>
# Execute command in pod
kubectl exec -it <pod-name> -- powershell
# Delete deployment
kubectl delete -f deployment.yaml
ConfigMaps and Secrets
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
database_server: "sql.contoso.com"
database_name: "AppDB"
log_level: "Information"
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
database_password: "P@ssw0rd123!"
api_key: "abc123xyz789"
# deployment with configmap and secret
apiVersion: apps/v1
kind: Deployment
metadata:
name: mywebapp
spec:
template:
spec:
containers:
- name: mywebapp
image: contoso/mywebapp:1.0
env:
- name: DatabaseServer
valueFrom:
configMapKeyRef:
name: app-config
key: database_server
- name: DatabasePassword
valueFrom:
secretKeyRef:
name: app-secrets
key: database_password
Container Networking
Docker Network Types
# List networks
docker network ls
# Create NAT network (default)
docker network create -d nat mynat
# Create Transparent network (external connectivity)
docker network create -d transparent mytransparent
# Create Overlay network (multi-host)
docker network create -d overlay --attachable myoverlay
# Inspect network
docker network inspect nat
# Run container with specific network
docker run -d --network=mynat --name web1 nginx:nanoserver
# Connect container to network
docker network connect mynat web1
# Disconnect container
docker network disconnect mynat web1
Container DNS and Service Discovery
# Create custom network with DNS
docker network create --driver nat --subnet=172.20.0.0/16 mynet
# Run containers on same network
docker run -d --network=mynet --name database mcr.microsoft.com/mssql/server:2019-latest
docker run -d --network=mynet --name webapp contoso/mywebapp:1.0
# Containers can communicate using container names as hostnames
# From webapp container: ping database
Port Mapping
# Map host port to container port
docker run -d -p 8080:80 contoso/mywebapp:1.0
# Map all exposed ports randomly
docker run -d -P contoso/mywebapp:1.0
# View port mappings
docker port <container-name>
# Multiple port mappings
docker run -d `
-p 8080:80 `
-p 8443:443 `
contoso/mywebapp:1.0
Persistent Storage
Docker Volumes
# Create volume
docker volume create mydata
# List volumes
docker volume ls
# Inspect volume
docker volume inspect mydata
# Run container with volume
docker run -d `
--name database `
-v mydata:C:\data `
mcr.microsoft.com/mssql/server:2019-latest
# Backup volume
docker run --rm `
-v mydata:C:\source `
-v C:\Backup:C:\backup `
mcr.microsoft.com/windows/servercore:ltsc2022 `
powershell -Command "Compress-Archive -Path C:\source\* -DestinationPath C:\backup\mydata.zip"
# Remove volume
docker volume rm mydata
# Remove unused volumes
docker volume prune
Bind Mounts
# Mount host directory to container
docker run -d `
--name webapp `
-v C:\HostData:C:\app\data `
contoso/mywebapp:1.0
# Read-only mount
docker run -d `
-v C:\Config:C:\app\config:ro `
contoso/mywebapp:1.0
Kubernetes Persistent Volumes
# persistent-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-data
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: "C:\\k8s-data"
type: DirectoryOrCreate
# persistent-volume-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: manual
# deployment with PVC
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
image: contoso/myapp:1.0
volumeMounts:
- name: data-volume
mountPath: C:\app\data
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: pvc-data
Container Registry
Azure Container Registry
# Login to ACR
az acr login --name contosoregistry
# Tag image for ACR
docker tag contoso/mywebapp:1.0 contosoregistry.azurecr.io/mywebapp:1.0
# Push image to ACR
docker push contosoregistry.azurecr.io/mywebapp:1.0
# Pull image from ACR
docker pull contosoregistry.azurecr.io/mywebapp:1.0
# Create Kubernetes secret for ACR
kubectl create secret docker-registry acr-secret `
--docker-server=contosoregistry.azurecr.io `
--docker-username=<service-principal-id> `
--docker-password=<service-principal-password>
# Use secret in deployment
# spec:
# imagePullSecrets:
# - name: acr-secret
Private Docker Registry
# Run private registry container
docker run -d `
-p 5000:5000 `
--name registry `
-v C:\registry:C:\registry `
registry:latest
# Tag image for private registry
docker tag contoso/mywebapp:1.0 localhost:5000/mywebapp:1.0
# Push to private registry
docker push localhost:5000/mywebapp:1.0
# Pull from private registry
docker pull localhost:5000/mywebapp:1.0
Hybrid Windows/Linux Workloads
Docker Compose with Mixed Containers
docker-compose.yml:
version: '3.8'
services:
# Linux container
database:
image: postgres:14
platform: linux
environment:
POSTGRES_PASSWORD: P@ssw0rd123!
volumes:
- db-data:/var/lib/postgresql/data
networks:
- app-network
# Windows container
webapp:
image: contoso/mywebapp:1.0
platform: windows
ports:
- "8080:80"
environment:
- ConnectionStrings__Database=Host=database;Database=myapp;Username=postgres;Password=P@ssw0rd123!
depends_on:
- database
networks:
- app-network
volumes:
db-data:
networks:
app-network:
driver: nat
# Start services
docker-compose up -d
# View logs
docker-compose logs
# Stop services
docker-compose down
Monitoring and Troubleshooting
Container Diagnostics
# View container resource usage
docker stats
# Inspect container
docker inspect <container-id>
# View container processes
docker top <container-name>
# Copy files from container
docker cp <container-name>:C:\logs\app.log C:\Temp\
# Copy files to container
docker cp C:\Config\app.config <container-name>:C:\app\
# Export container filesystem
docker export <container-name> -o container.tar
Kubernetes Monitoring
# View cluster events
kubectl get events --sort-by=.metadata.creationTimestamp
# View node status
kubectl get nodes -o wide
# View resource usage
kubectl top nodes
kubectl top pods
# Describe pod (detailed troubleshooting)
kubectl describe pod <pod-name>
# View pod logs (previous container)
kubectl logs <pod-name> --previous
# Port forward for debugging
kubectl port-forward pod/<pod-name> 8080:80
Key Takeaways
- Windows Server containers enable portable application deployment
- Process isolation shares kernel, Hyper-V isolation provides stronger security
- Docker simplifies container image creation and management
- Kubernetes orchestrates multi-container applications at scale
- Container networking enables service discovery and communication
- Persistent volumes retain data across container restarts
- Azure Container Registry provides secure image storage
- Docker Compose simplifies multi-container applications
- Hybrid workloads combine Windows and Linux containers
- Monitoring tools help diagnose container issues
Next Steps
- Containerize existing .NET applications
- Set up Azure Container Registry
- Deploy Kubernetes cluster for production
- Implement CI/CD pipeline for container images
- Configure monitoring and logging
- Establish container security policies
Additional Resources
- Windows Containers
- Docker Documentation
- Kubernetes on Windows
- Azure Container Registry
- Docker Compose
Build. Ship. Run. Anywhere.