Declarative. Explicit. Boring (in the best way). Windows conversions fail when networking and drivers are left to luck. hyper2kvm makes both fully declarative using YAML + JSON, with no hidden magic.
This document explains how Windows networking and drivers are configured, how YAML references JSON, and how everything fits into the larger hyper2kvm pipeline.
For Windows VM migration, you need:
flowchart TD
Start[Windows VM Migration] --> Detect[Detect Windows Version]
Detect --> LoadJSON[Load virtio-win-versions.json]
LoadJSON --> SelectDrivers[Select VirtIO Drivers]
SelectDrivers --> InjectStorage[Inject VirtIO Storage Driver]
InjectStorage --> ModifyRegistry[Modify Registry<br/>CriticalDeviceDatabase]
ModifyRegistry --> InjectNetwork[Inject VirtIO Network Driver]
InjectNetwork --> TwoPhase{Two-Phase Boot?}
TwoPhase -->|Yes| Phase1[Phase 1: SATA Boot]
TwoPhase -->|No| DirectBoot[Direct VirtIO Boot]
Phase1 --> InstallDrivers[Windows Installs Drivers]
InstallDrivers --> Phase2[Phase 2: VirtIO Boot]
Phase2 --> NetworkReady[Network Ready]
DirectBoot --> NetworkReady
NetworkReady --> Success[Migration Complete]
style Start fill:#e1f5e1
style Success fill:#c8e6c9
style ModifyRegistry fill:#ffebee
style InjectStorage fill:#e3f2fd
style InjectNetwork fill:#f3e5f5
virtio-win-versions.json contains driver paths and compatibilityCriticalDeviceDatabasehyper2kvm follows a strict separation of concerns:
| Layer | Responsibility | Format |
|---|---|---|
| YAML | Orchestration & intent | YAML |
| JSON | Stable structured knowledge | JSON |
| Code | Execution engine | Python |
Why this matters:
These mechanisms apply when:
You care about:
Windows networking is not modified directly offline. Instead, hyper2kvm stages a first-boot configuration that is applied once Windows boots under KVM.
This avoids registry archaeology and matches how Windows actually wants to be configured.
command: local
vmdk: /images/windows.vmdk
windows: true
win_net_override: ./net/windows-network.json
windows-network.json){
"schema": 1,
"mode": "static",
"static": {
"address": "192.168.100.50/24",
"gateway": "192.168.100.1",
"dns_servers": ["10.0.0.53", "8.8.8.8"]
}
}
File is staged into the guest (example):
C:\hyper2kvm\net\network_override.json
A first-boot helper applies it using:
netshAfter the first successful application, the override is not re-applied.
Useful when embedding everything into a single YAML file (systemd, CI, Ansible).
win_net_json: >-
{"schema":1,"mode":"dhcp",
"dhcp":{"dns_servers":["10.0.0.53"]}}
Behavior:
workdir/win_net_override{
"schema": 1,
"mode": "dhcp | static",
"dhcp": {
"dns_servers": ["x.x.x.x"]
},
"static": {
"address": "IP/CIDR",
"gateway": "IP",
"dns_servers": ["IP", "IP"]
}
}
Driver handling is explicit and data-driven.
No guessing. No OS-string hacks. No hard-coded PNP IDs.
windows_drivers:
virtio:
metadata_json: ./drivers/virtio-drivers.json
virtio-drivers.json){
"schema": 1,
"default_os_bucket": "win11",
"os_buckets": {
"win11": { "min_build": 22000 },
"win10": { "min_build": 10240, "max_build": 21999 },
"win2019": { "min_build": 17763 },
"win2022": { "min_build": 20348 }
},
"roles": {
"storage_virtio_scsi": {
"inf_globs": ["vioscsi\\\\**\\\\*.inf"],
"pnp_ids": ["PCI\\\\VEN_1AF4&DEV_1004"]
},
"storage_virtio_blk": {
"inf_globs": ["viostor\\\\**\\\\*.inf"],
"pnp_ids": ["PCI\\\\VEN_1AF4&DEV_1001"]
},
"net_virtio": {
"inf_globs": ["NetKVM\\\\**\\\\*.inf"],
"pnp_ids": ["PCI\\\\VEN_1AF4&DEV_1000"]
},
"balloon": {
"inf_globs": ["Balloon\\\\**\\\\*.inf"],
"pnp_ids": ["PCI\\\\VEN_1AF4&DEV_1002"]
},
"rng": {
"inf_globs": ["viorng\\\\**\\\\*.inf"],
"pnp_ids": ["PCI\\\\VEN_1AF4&DEV_1005"]
},
"serial": {
"inf_globs": ["vioserial\\\\**\\\\*.inf"],
"pnp_ids": ["PCI\\\\VEN_1AF4&DEV_1003"]
}
}
}
Some vendors ship custom SUBSYS IDs. These belong in data, not code.
virtio-vendors.json){
"vendors": {
"acme-oem": {
"roles": {
"net_virtio": {
"pnp_ids": [
"PCI\\\\VEN_1AF4&DEV_1000&SUBSYS_00000001",
"PCI\\\\VEN_1AF4&DEV_1000&SUBSYS_12345678"
]
}
}
}
}
}
windows_drivers:
virtio:
metadata_json: ./drivers/virtio-drivers.json
vendor_overrides_json: ./drivers/virtio-vendors.json
active_vendor: acme-oem
command: local
vmdk: /images/windows.vmdk
output_dir: ./out
windows: true
# Network configuration
win_net_override: ./net/windows-network.json
# 🧰 Driver metadata
windows_drivers:
virtio:
metadata_json: ./drivers/virtio-drivers.json
vendor_overrides_json: ./drivers/virtio-vendors.json
active_vendor: acme-oem
flatten: true
to_output: windows-kvm.qcow2
out_format: qcow2
compress: true
checksum: true
For Windows migrations: