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