Introduction

Docker Compose is a tool for defining and running multi-container Docker applications using a declarative YAML file. On Windows Server 2022, Docker Compose allows you to define complex applications — such as an IIS web server paired with a SQL Server database — in a single docker-compose.yml file, and manage the entire stack with simple commands. This guide covers installing Docker Compose on Windows Server 2022, writing Compose files for Windows containers, handling Windows-style volume paths, environment variable files, networking, and running Compose with Hyper-V isolation in production.

Installing Docker Compose on Windows Server 2022

Docker Compose is distributed as a standalone binary for Windows. The recommended installation method is to download the latest release from GitHub using PowerShell. First, create a directory for the binary and download it:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$composeVersion = "2.27.0"
$dest = "$Env:ProgramFilesDockerdocker-compose.exe"
Invoke-WebRequest -Uri "https://github.com/docker/compose/releases/download/v${composeVersion}/docker-compose-windows-x86_64.exe" -OutFile $dest

Verify Docker Compose is accessible (it should be in a directory already on the PATH if installed to Program FilesDocker):

docker-compose --version

Alternatively, if Python is installed on the server, you can install Docker Compose via pip, though the standalone binary is preferred for server environments:

pip install docker-compose

Note that modern Docker Desktop bundles Compose as a Docker CLI plugin (accessed via docker compose without the hyphen). On Windows Server without Docker Desktop, the standalone binary is the correct tool and uses docker-compose with the hyphen. Both syntaxes work for the operations covered here.

docker-compose.yml Syntax for Windows

A docker-compose.yml file for Windows containers follows the same Compose specification as Linux, with two key differences: base images must be Windows container images, and paths use Windows-style drive letters and backslashes (which must be escaped in YAML). Here is the basic structure of a Windows Compose file:

version: "3.8"

services:
  webapp:
    image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022
    ports:
      - "80:80"
    volumes:
      - C:inetpubmysite:C:inetpubwwwroot
    environment:
      - APP_ENV=production
    networks:
      - frontend

networks:
  frontend:
    driver: nat

Important YAML quoting rules for Windows paths: backslashes in YAML strings must be escaped as double backslashes, or the entire path must be quoted. The volume paths above are unquoted and Docker Compose on Windows handles them correctly; however, if paths contain spaces, they must be quoted. The network driver must be a Windows-compatible driver — nat is the correct equivalent of the default Linux bridge driver.

Defining Windows Services in Compose

Each service in the Compose file maps to a container. For Windows workloads, specify the image tag corresponding to your Windows Server version (ltsc2022) to ensure kernel compatibility. You can also build images inline by pointing to a Dockerfile:

version: "3.8"

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile.windows
    image: mycompany/myapi:1.0
    ports:
      - "8080:80"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ConnectionStrings__Default=Server=db;Database=AppDB;User Id=sa;Password=StrongP@ss1;
    depends_on:
      - db
    networks:
      - appnet

  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - MSSQL_SA_PASSWORD=StrongP@ss1
      - MSSQL_PID=Developer
    ports:
      - "1433:1433"
    volumes:
      - sqldata:C:data
    networks:
      - appnet

volumes:
  sqldata:

networks:
  appnet:
    driver: nat

Note that the SQL Server image (mcr.microsoft.com/mssql/server) is a Linux container image. Running it alongside Windows container services in the same Compose file requires LCOW (Linux Containers on Windows) to be enabled on the Docker host, or the SQL Server container must run on a separate Linux host. For purely Windows-native environments, consider using Windows-compatible alternatives or a separate SQL Server on Windows installation.

Windows-Style Volume Paths

Volume mounts in Windows containers use drive letter paths. Named volumes are managed by Docker and stored under C:ProgramDataDockervolumes on the host. Bind mounts reference actual host filesystem paths. In a Compose file, bind mounts for Windows look like this:

services:
  webapp:
    image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022
    volumes:
      - type: bind
        source: C:Projectsmyappwwwroot
        target: C:inetpubwwwroot
      - type: bind
        source: C:Projectsmyapplogs
        target: C:inetpublogs

Using the long-form volume syntax (type, source, target) is clearer and avoids ambiguity with Windows paths that contain colons. The short-form : syntax also works but can confuse YAML parsers if the paths contain unescaped characters.

For named volumes (persisted across container restarts and removed only when explicitly deleted):

services:
  api:
    volumes:
      - appdata:C:appdata

volumes:
  appdata:
    driver: local

Environment Files

Storing sensitive values such as database passwords in a Compose file is not recommended for production. Docker Compose supports .env files that are loaded automatically from the same directory as the Compose file, and env_file directives that load named files for specific services:

# .env file in the project root
APP_ENV=production
DB_PASSWORD=StrongP@ss1
API_KEY=abc123xyz

Reference these variables in docker-compose.yml using ${VARIABLE} syntax:

services:
  api:
    environment:
      - APP_ENV=${APP_ENV}
      - DB_PASSWORD=${DB_PASSWORD}

Alternatively, use an env_file directive to load all variables from a named file into the container’s environment:

services:
  api:
    env_file:
      - ./config/api.env

The .env file is not committed to source control — add it to .gitignore. Provide a .env.example file with placeholder values for documentation.

IIS and SQL Server Compose Example

Here is a complete production-oriented Compose file for an ASP.NET application hosted in IIS alongside SQL Server, using Windows containers exclusively:

version: "3.8"

services:
  iis:
    build:
      context: ./iis
      dockerfile: Dockerfile
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - type: bind
        source: C:LogsIIS
        target: C:inetpublogsLogFiles
    environment:
      - DB_SERVER=sqlserver
      - DB_NAME=AppDatabase
    env_file:
      - .env
    depends_on:
      - sqlserver
    networks:
      - webnet
    restart: always

  sqlserver:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - MSSQL_SA_PASSWORD=${DB_PASSWORD}
      - MSSQL_PID=Standard
    ports:
      - "1433:1433"
    volumes:
      - sqldata:C:data
    networks:
      - webnet
    restart: always

volumes:
  sqldata:

networks:
  webnet:
    driver: nat

Docker Compose Commands

Start all services defined in docker-compose.yml in detached mode:

docker-compose up -d

Build images before starting (forces rebuild even if image exists):

docker-compose up -d --build

Stop and remove containers, networks, and the default volume:

docker-compose down

Stop and remove everything including named volumes (destroys data):

docker-compose down -v

View logs for all services (follow mode):

docker-compose logs -f

View logs for a specific service:

docker-compose logs -f iis

List running services and their status:

docker-compose ps

Scale a service to multiple replicas (requires Swarm or a load balancer for traffic distribution):

docker-compose up -d --scale iis=3

Execute a command inside a running service container:

docker-compose exec iis powershell

Restart a single service without restarting the whole stack:

docker-compose restart iis

Compose Networking on Windows

By default, Docker Compose creates a single NAT network for the stack and attaches all services to it. Services can reach each other using their service name as a hostname — Docker’s embedded DNS resolves service names to container IPs within the same network. For example, the IIS container can reach SQL Server at the hostname sqlserver on port 1433.

To define multiple networks and control which services are connected to which:

networks:
  frontend:
    driver: nat
  backend:
    driver: nat

services:
  iis:
    networks:
      - frontend
      - backend
  sqlserver:
    networks:
      - backend

This isolates SQL Server to the backend network — it is only reachable from services that are also on the backend network, not directly from the external frontend.

Docker Compose with Hyper-V Isolation

Docker Compose does not have a built-in isolation field in the Compose specification. To run Compose services with Hyper-V isolation, you must set the isolation parameter in the service definition using Compose’s deploy.resources extensions or pass it via the security_opt workaround. The cleanest supported approach on standalone Docker (not Swarm) is to configure the Docker daemon’s default isolation mode:

# C:ProgramDatadockerconfigdaemon.json
{
  "exec-opts": ["isolation=hyperv"]
}

Restart Docker after editing daemon.json:

Restart-Service docker

With this configuration all containers started by Docker Compose will use Hyper-V isolation by default. Verify the isolation mode after bringing up the stack:

docker inspect --format "{{.HostConfig.Isolation}}" compose_iis_1

Production Considerations

In production, Docker Compose on a single Windows Server host is suitable for small deployments. For multi-host orchestration, consider Docker Swarm or Kubernetes with Windows nodes. Several practices improve reliability:

Always set restart policies (restart: always or restart: unless-stopped) for services that should survive host reboots. Use health checks to allow dependent services to wait for readiness:

services:
  sqlserver:
    healthcheck:
      test: ["CMD", "sqlcmd", "-S", "localhost", "-U", "sa", "-P", "${DB_PASSWORD}", "-Q", "SELECT 1"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s

Pin image versions precisely (ltsc2022-specific tags) to prevent unexpected updates from breaking the stack. Store the docker-compose.yml and .env.example in source control, but never commit the .env file with real credentials.

Conclusion

Docker Compose on Windows Server 2022 streamlines multi-container Windows application management. With a single YAML file you can define services, networks, volumes, environment variables, and build contexts, then bring the entire stack up or down with one command. The combination of Windows container base images, NAT networking, and environment file support makes Compose a practical tool for deploying and managing Windows-native containerized workloads in both development and production environments.