← Back to blog

Windows Server runs on kcore as a normal VM. The guest disk is built once, outside the cluster, from installation media you are licensed to use. The node then boots that disk on Cloud Hypervisor. Three things have to be true or Windows stops at the boot manager: the kcore ISO must include the VM module, every guest must get Hyper-V CPU enlightenments, and the uploaded image must already contain virtio drivers.

The disk recipe is kcorehypervisor/windows-guest. It installs Server 2022 under QEMU with virtio-win 0.1.240, turns on the serial console (SAC) and OpenSSH, and leaves a raw disk. Newer virtio-win builds fail on Cloud Hypervisor. The recordings below are the kcore side: build the ISO, declare the VM in Terraform, then watch it boot.

1. Build the kcore ISO

The installer ISO is a NixOS image. It copies the ch-vm module onto the node, including the Cloud Hypervisor unit that every VM uses. Build it from a checkout of kcore:

nix build .#nixosConfigurations.kcore-iso.config.system.build.isoImage

The result is a bootable ISO with volume id KCORE.

2. Declare the VM in Terraform

You do not edit the node. A current kcore image already starts every guest with Hyper-V CPU enlightenments, which Windows needs and Linux ignores. The configuration you write is a kcore_vm resource. Point it at the image uploaded to the node. Leave cloud-init and SSH keys unset: Windows logs in as the Administrator account from the image, not as a cloud-init user.

terraform {
  required_providers {
    kcore = {
      source  = "kcorehypervisor/kcore"
      version = "0.3.0"
    }
  }
}

provider "kcore" {
  controller_address = "192.168.40.107:9090"
  tls_cert_path      = var.tls_cert_path
  tls_key_path       = var.tls_key_path
  tls_ca_path        = var.tls_ca_path
}

resource "kcore_vm" "win" {
  name               = "win-01"
  cpu                = 2
  memory_bytes       = 4 * 1024 * 1024 * 1024
  storage_backend    = "lvm"
  storage_size_bytes = 40 * 1024 * 1024 * 1024

  image_path   = "/var/lib/kcore/images/windows-server-2022.qcow2"
  image_format = "qcow2"
  target_node  = "node-107"

  nic {
    network = "default"
    model   = "virtio"
  }

  desired_state = "running"
}

# Windows 11 is the same resource and a different image.
# The node does not present a TPM or Secure Boot, so the image has to be
# built with those setup checks skipped. See the windows-guest recipe.
resource "kcore_vm" "win11" {
  name               = "win11-01"
  cpu                = 2
  memory_bytes       = 4 * 1024 * 1024 * 1024
  storage_backend    = "lvm"
  storage_size_bytes = 40 * 1024 * 1024 * 1024

  image_path   = "/var/lib/kcore/images/windows-11.qcow2"
  image_format = "qcow2"
  target_node  = "node-107"

  nic {
    network = "default"
    model   = "virtio"
  }

  desired_state = "running"
}

storage_size_bytes is the virtual size of the disk, 40 GiB here, not the size of the compact qcow2 file. terraform apply creates the VM on the controller. Windows 11 does not need another provider argument. Upload its qcow2 and change image_path.

3. Create the Windows VM

Upload the qcow2 from the recipe, then create the VM. --storage-size-bytes is the virtual size of the disk (40 GiB here), not the size of the compact file. LVM converts that qcow2 to a raw volume before Cloud Hypervisor opens it. The guest address on a NAT network is reachable from the node. kctl console is the serial port, and SAC comes up once Windows has started.

kctl --node 192.168.40.107:9091 node upload-image \
  -f windows-server-2022.qcow2 \
  --name windows-server-2022.qcow2 \
  --format qcow2

kctl create vm win-01 \
  --cpu 2 --memory 4G --network default \
  --storage-backend lvm --storage-size-bytes 42949672960 \
  --target-node node-107 \
  --image-path /var/lib/kcore/images/windows-server-2022.qcow2 \
  --image-format qcow2

4. IIS from the command line

IIS is an inbox feature. From an Administrator command prompt on the guest:

powershell -Command "Install-WindowsFeature Web-Server -IncludeManagementTools"

The page below is the stock IIS welcome site. The workstation cannot open the guest address directly, so the browser is pointed at a port forward through the node (ssh -N -L 8080:<guest-ip>:80 root@<node>).

Default IIS welcome page on Windows Server 2022
Default IIS welcome page on Windows Server 2022.