Introduction to Docker on Windows Server 2019

Docker is the leading container platform that provides tools for building, running, and managing containers. On Windows Server 2019, Docker provides native Windows container support alongside the ability to run Linux containers through Hyper-V isolation. Docker on Windows Server 2019 supports Docker Compose for defining and running multi-container applications, Docker Swarm for container orchestration across multiple hosts, and integration with container registries including Docker Hub, Azure Container Registry, and private registries. This guide covers a comprehensive Docker setup on Windows Server 2019 from installation through building production-ready container configurations.

Installing the Containers Feature and Docker

The Containers Windows feature must be enabled before Docker can run. Install it and reboot:

Install-WindowsFeature -Name containers -Restart

After reboot, install Docker Engine. The recommended method for Windows Server is via the DockerMsftProvider PowerShell module:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
Install-Package -Name docker -ProviderName DockerMsftProvider -Force
Restart-Computer -Force

Alternatively, install Docker Community Edition from the official Docker channel:

Invoke-WebRequest -UseBasicParsing "https://raw.githubusercontent.com/microsoft/Windows-Containers/Main/helpful_tools/Install-DockerCE/install-docker-ce.ps1" -o install-docker-ce.ps1
.install-docker-ce.ps1

Verify Docker version and service:

docker version
docker info
Get-Service docker

Configuring the Docker Daemon

Customize Docker’s behavior by editing the daemon configuration file daemon.json located at C:ProgramDataDockerconfigdaemon.json. Common configurations include setting the default container registry mirror, configuring DNS servers for containers, setting the log driver, and specifying the data directory. Create or edit daemon.json:

{
  "dns": ["8.8.8.8", "8.8.4.4"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "data-root": "D:\DockerData",
  "hosts": ["tcp://0.0.0.0:2375", "npipe://"],
  "tlsverify": false,
  "exec-opts": ["isolation=process"],
  "storage-driver": "windowsfilter"
}

Restart Docker after modifying daemon.json:

Restart-Service docker

Working with Docker Images

Docker images are the templates from which containers are created. Learn to work with the image lifecycle including pulling, tagging, inspecting, and removing images:

docker pull mcr.microsoft.com/windows/servercore:ltsc2019
docker pull mcr.microsoft.com/dotnet/aspnet:6.0-windowsservercore-ltsc2019
docker images --all
docker image inspect mcr.microsoft.com/windows/servercore:ltsc2019
docker image history mcr.microsoft.com/windows/servercore:ltsc2019
docker image prune -a --filter "until=720h"

Search for available images on Docker Hub:

docker search microsoft
docker search --filter is-official=true --filter stars=10 windows

Writing Effective Dockerfiles

Dockerfiles define how custom images are built. Write optimized Dockerfiles by ordering instructions from least to most frequently changing to maximize build cache reuse. Here is a Dockerfile for a .NET 6 web application on Windows Server 2019:

FROM mcr.microsoft.com/dotnet/sdk:6.0-windowsservercore-ltsc2019 AS build
WORKDIR /src
COPY ["MyApp/MyApp.csproj", "MyApp/"]
RUN dotnet restore "MyApp/MyApp.csproj"
COPY . .
WORKDIR /src/MyApp
RUN dotnet build "MyApp.csproj" -c Release -o /app/build
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:6.0-windowsservercore-ltsc2019 AS final
WORKDIR /app
COPY --from=build /app/publish .
EXPOSE 80
ENTRYPOINT ["dotnet", "MyApp.dll"]

Build the image from the Dockerfile:

docker build -t contoso/myapp:1.0 -f Dockerfile .
docker build -t contoso/myapp:1.0 --no-cache --build-arg BUILD_DATE=$(Get-Date -Format yyyyMMdd) .

Managing Container Lifecycle

Manage containers through their complete lifecycle from creation to removal. Important Docker container commands:

docker run -d --name web01 --restart unless-stopped -p 80:80 contoso/myapp:1.0
docker run -d --name db01 -e ACCEPT_EULA=Y -e SA_PASSWORD=P@ssw0rd123! -p 1433:1433 mcr.microsoft.com/mssql/server:2019-latest
docker ps -a --format "table {{.Names}}t{{.Status}}t{{.Ports}}"
docker start web01
docker stop web01
docker restart web01
docker pause web01
docker unpause web01
docker rm -f web01
docker exec -it web01 powershell
docker attach web01

Copy files between host and container:

docker cp web01:C:inetpubwwwrootindex.html C:Tempindex.html
docker cp C:NewIndex.html web01:C:inetpubwwwrootindex.html

Docker Compose for Multi-Container Applications

Docker Compose simplifies management of multi-container applications by defining all services, networks, and volumes in a single YAML file. Install Docker Compose on Windows Server 2019:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-windows-x86_64.exe" -OutFile "$env:ProgramFilesDockerdocker-compose.exe"
docker compose version

Create a docker-compose.yml for a web application with a SQL Server database:

version: "3.8"
services:
  web:
    image: contoso/myapp:1.0
    ports:
      - "80:80"
    depends_on:
      - db
    environment:
      - ConnectionStrings__DefaultConnection=Server=db;Database=AppDB;User=sa;Password=P@ssw0rd123!
    networks:
      - appnet
    restart: unless-stopped
  db:
    image: mcr.microsoft.com/mssql/server:2019-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=P@ssw0rd123!
    volumes:
      - sqldata:C:data
    networks:
      - appnet
    restart: unless-stopped
networks:
  appnet:
    driver: nat
volumes:
  sqldata:

Manage the Compose application:

docker compose up -d
docker compose ps
docker compose logs web
docker compose down
docker compose down -v

Setting Up Docker Swarm

Docker Swarm provides native container orchestration across multiple Docker hosts. Initialize a Swarm on the manager node:

docker swarm init --advertise-addr 192.168.1.50
# Copy the join token output and run it on worker nodes:
docker swarm join --token SWMTKN-1-xxxxx 192.168.1.50:2377

Verify Swarm status and nodes:

docker node ls
docker info | findstr Swarm

Deploy a service across the Swarm:

docker service create --name webapp --replicas 3 --publish 80:80 contoso/myapp:1.0
docker service ls
docker service ps webapp
docker service scale webapp=5

Deploy using a Docker Stack from a Compose file:

docker stack deploy -c docker-compose.yml CorpApp
docker stack ls
docker stack services CorpApp
docker stack rm CorpApp

Securing Docker on Windows Server 2019

Secure your Docker environment by enabling TLS for the Docker daemon to prevent unauthorized access to the Docker socket. Generate TLS certificates and configure daemon.json:

mkdir C:Dockercerts
cd C:Dockercerts
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=192.168.1.50" -sha256 -new -key server-key.pem -out server.csr
echo subjectAltName = DNS:docker-host,IP:192.168.1.50 > extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf

Update daemon.json to enable TLS:

{
  "tlsverify": true,
  "tlscacert": "C:\Docker\certs\ca.pem",
  "tlscert": "C:\Docker\certs\server-cert.pem",
  "tlskey": "C:\Docker\certs\server-key.pem",
  "hosts": ["tcp://0.0.0.0:2376", "npipe://"]
}

Docker Monitoring and Troubleshooting

Monitor Docker container health and resource usage using built-in commands and Windows Event Viewer:

docker stats --no-stream
docker system df
docker system prune -af --volumes
docker events --filter type=container --filter event=die
Get-EventLog -LogName Application -Source Docker -Newest 20 | Select-Object TimeGenerated, Message

Check Docker daemon logs:

Get-WinEvent -LogName "Microsoft-Windows-Hyper-V-Compute-Operational" -MaxEvents 20 | Format-List TimeCreated, Message