Windows Server Containers and Docker: Modern Application Deployment
## Building and Running Containers
```powershell
## Build image
docker build -t contoso/mywebapp:1.0 .
## View images
docker images
## Run container
docker run -d `
```text
--name mywebapp `
-p 8080:80 `
contoso/mywebapp:1.0
Expected output:
[+] Building 24.3s (12/12) FINISHED
=> naming to docker.io/library/myapp:latest
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
```dockerfile
## 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"]
Expected output:
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:04.12
IIS Container Example
## IIS with ASP.NET
FROM mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022
## Install ASP.NET
RUN powershell -Command `
```text
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
```powershell
## 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:
```yaml
app: mywebapp```
spec:
replicas: 3
selector:
```yaml
matchLabels:
app: mywebapp```
template:
```yaml
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:**
```yaml
apiVersion: v1
kind: Service
metadata:
name: mywebapp-service
spec:
type: LoadBalancer
selector:
```yaml
app: mywebapp```
ports:
- protocol: TCP
```yaml
port: 80
targetPort: 80
### Deploying Application
```powershell
## 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
Expected output:
deployment.apps/myapp-api created
service/myapp-api-svc created
ConfigMaps and Secrets
Figure: Azure Key Vault blade – secrets list with expiry dates and access policies.
## 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:
```yaml
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
```powershell
## 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 `
```text
-p 8080:80 `
-p 8443:443 `
contoso/mywebapp:1.0
## Persistent Storage
### Docker Volumes
```powershell
## Create volume
docker volume create mydata
## List volumes
docker volume ls
## Inspect volume
docker volume inspect mydata
## Run container with volume
docker run -d `
```text
--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
```powershell
## Mount host directory to container
docker run -d `
```text
--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
```yaml
## persistent-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-data
spec:
capacity:
```yaml
storage: 10Gi```
accessModes:
```text
- ReadWriteOnce```
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
```yaml
path: "C:\\k8s-data"
type: DirectoryOrCreate
```yaml
## persistent-volume-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-data
spec:
accessModes:
```text
- ReadWriteOnce```
resources:
```yaml
requests:
storage: 5Gi```
storageClassName: manual
## deployment with PVC
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
```yaml
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
```powershell
## 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 `
```text
--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
```powershell
## Run private registry container
docker run -d `
```text
-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:**
```yaml
version: '3.8'
services:
# Linux container
database:
```yaml
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
```powershell
## 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
Figure: AKS cluster workloads – pod status, node pools, and horizontal autoscaler.
## 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
Architecture Decision and Tradeoffs
When designing server infrastructure solutions with Windows Server, consider these key architectural trade-offs:
| Approach | Best For | Tradeoff |
|---|---|---|
| Managed / platform service | Rapid delivery, reduced ops burden | Less customisation, potential vendor lock-in |
| Custom / self-hosted | Full control, advanced tuning | Higher operational overhead and cost |
Recommendation: Start with the managed approach for most workloads and move to custom only when specific requirements demand it.
Validation and Versioning
- Last validated: April 2026
- Validate examples against your tenant, region, and SKU constraints before production rollout.
- Keep module, CLI, and SDK versions pinned in automation pipelines and review quarterly.
Security and Governance Considerations
- Apply least-privilege access using RBAC roles and just-in-time elevation for admin tasks.
- Store secrets in managed secret stores and avoid embedding credentials in scripts or source files.
- Enable audit logging, data protection policies, and periodic access reviews for regulated workloads.
Cost and Performance Notes
- Define budgets and alerts, then monitor usage and cost trends continuously after go-live.
- Baseline performance with synthetic and real-user checks before and after major changes.
- Scale resources with measured thresholds and revisit sizing after usage pattern changes.
Official Microsoft References
- https://learn.microsoft.com/windows-server/
- https://learn.microsoft.com/windows/security/
- https://learn.microsoft.com/azure/azure-arc/
Public Examples from Official Sources
- These examples are sourced from official public Microsoft documentation and sample repositories.
- Documentation examples: https://learn.microsoft.com/windows-server/
- Sample repositories: https://github.com/microsoft/Windows-Containers
- Prefer adapting these examples to your tenant, subscriptions, and governance requirements before production use.
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.
Discussion