Windows via Kryton
Zeus OS can create Windows machines by calling Kryton’s REST API (no Kryton UI embed). Day-2 (VNC, NodePort RDP, GuestKit) stays in Zeus.
When to use it
- Prefer Kryton catalog in the Create VM wizard when Settings → Network shows Kryton Connected.
- Use Build with Kryton on Image Factory for golden-image builds Kryton owns (dockur install → Sysprep → CDI bootstrap).
- Keep Zeus Autounattend / ISO / Zeus Image Factory PVC paths when Kryton is not deployed.
Catalog (12 dockur IDs)
Zeus built-in Windows templates match Kryton’s catalog (template id = Kryton imageId):
| Desktop | Server |
|---|---|
windows-11-enterprise, windows-11-pro, windows-11-ltsc |
windows-server-2025, windows-server-2022 |
windows-10-enterprise, windows-10-pro, windows-10-ltsc |
windows-server-2019, windows-server-2016 |
windows-tiny11, windows-tiny11-core |
Legacy Zeus-only IDs (windows-11, windows-10, windows-server-2012-r2, windows-server-sql-2022) are no longer primary marketplace templates.
Operator wiring
| Helm / env | Purpose |
|---|---|
config.krytonUrl / ZEUS_OS_KRYTON_URL |
Kryton base URL (lab kubevirt API often :9088) |
config.krytonApiKey or Secret / ZEUS_OS_KRYTON_API_KEY |
API bearer token |
config.krytonApiKeySecretName |
Prefer Secret (api-key key) over plaintext |
config.krytonProject / ZEUS_OS_KRYTON_PROJECT |
Default project (default) |
config.krytonEnabled: false / ZEUS_OS_KRYTON=off |
Kill-switch |
The API key stays on the server. The browser only calls /api/v1/kryton/* on Zeus.
Do not drop the API key based on URL shape (in-cluster vs host IP).
Lab harden (Kryton)
On the Kryton host, after deploy:
# Kryton repo
./scripts/deploy-remote.sh <host> <user> --quick --key
make harden-lab H=<host> U=<user>
# kubevirt API :9088, dockur lab :7088, token ~/.kryton/lab.token
Wire into Zeus (example):
TOKEN=$(ssh <user>@<host> 'cat ~/.kryton/lab.token')
kubectl -n zeus-os create secret generic zeus-os-kryton-apikey \
--from-literal=api-key="$TOKEN" --dry-run=client -o yaml | kubectl apply -f -
helm upgrade zeus-os ./chart -n zeus-os --reuse-values \
--set-string config.krytonUrl=http://<host>:9088 \
--set-string config.krytonApiKeySecretName=zeus-os-kryton-apikey \
--set-string config.krytonProject=default \
--set config.krytonEnabled=true
Verify: Settings → Network → Kryton Connected, or
GET /api/v1/kryton/status → configured: true, healthy: true.
Zeus API surface
| Route | Purpose |
|---|---|
GET /api/v1/kryton/status |
Configured / healthy / capabilities / doctor |
GET /api/v1/kryton/images |
Live catalog |
POST /api/v1/kryton/machines |
Create Windows machine |
GET/DELETE …/kryton/machines/{id} |
List / delete |
POST …/kryton/machines/{id}/{start\|stop\|reboot} |
Lifecycle |
GET/POST /api/v1/kryton/golden |
List / start golden |
GET /api/v1/kryton/golden/{id} |
Poll build |
POST /api/v1/kryton/golden/{id}/bootstrap |
Bootstrap DataSource into cluster |
Create body (kubevirt provider)
POST /kryton/machines (Zeus or Kryton) requires a disk size. Omitting it returns
disk.sizeGiB must be between 16 and 65536. The Create VM wizard sends
disk.sizeGiB from the catalog defaultDiskGiB (see useCreateVmSubmit).
{
"name": "win-lab-01",
"image": "windows-tiny11-core",
"project": "default",
"compute": { "cpu": 2, "memoryMiB": 4096 },
"disk": { "sizeGiB": 64 }
}
When Kryton’s provider is kubevirt, only images with a CDI DataSource in
kryton-images create successfully (GET /kryton/status → doctor
kubevirt-images).
Golden images — do not trust a one-minute “ready”
A real dockur install + Sysprep takes well over an hour. If GET /kryton/golden/{id}
flips to ready in ~1 minute, treat the qcow2 as suspect: VNC of a clone often shows
Windows Setup (“restarted unexpectedly”) instead of a desktop, and RDP never comes up.
Before bootstrap / create:
- Confirm the dockur console finished install + Sysprep (not stuck at “58% windows_setup”).
- Confirm
outputPathqcow2 is from a FINALIZE=1 capture (Krytonbuild-golden-image.shmust preserve parentFINALIZE/IMAGE_ID— see Krytondocs/GOLDEN-IMAGES.md). - Prefer
availability: stored+ a fresh sha after a long build over catalog aliases that share one path (lab sawwindows-11-proandwindows-tiny11-corepointing at the same core11 qcow2).
CDI bootstrap gotchas
| Symptom | Cause / fix |
|---|---|
Bootstrap / virtctl image-upload can’t reach uploadproxy |
Service is ClusterIP-only — port-forward or set an external uploadproxy URL |
| Import fails on small PVC | Virtual disk can be ~80 Gi — use a ≥96 Gi PVC |
IncompatibleVolumeModes on clone |
Golden DV was Filesystem; target expected Block (or reverse). Match volume modes on clone, or rebuild golden as Block |
| Doctor lists missing DataSources | Run POST /kryton/golden/{id}/bootstrap or Kryton bootstrap-kubevirt-images.sh after a real golden |
Day-2 (VNC + native RDP)
After create, open the VM in Zeus (VNC, native RDP NodePort, GuestKit) using Kryton’s
providerRef namespace/name (zeusVm deep-link). Guest agent tooling remains GuestKit /
QEMU-GA — not Kryton.
Native RDP (POST /api/v1/vms/{ns}/{name}/rdp-access/expose) needs all of:
- Guest listening on 3389 (finalized golden / RDP enabled — not mid-Setup).
- Masquerade port 3389 on the VM interface (
vm_spec_has_rdp_port; Zeus Windows create merges this automatically — hand-rolled VMs must add it and restart). - Service selector
vm.kubevirt.io/name=<vm>(KubeVirt always labels virt-launcher pods this way). Empty endpoints +No route to hoston the NodePort usually means a stalekubevirt.io/vm=selector.
Probe from the host: Service endpoints → pod IP:3389 (masquerade DNAT to 10.0.2.2) →
NodePort. Guest-agent absence is common on Tiny11; use VNC to confirm desktop vs Setup.
E2E catalog script
ZEUS_OS_E2E_INSECURE=1 ./scripts/e2e-kryton-catalog.sh https://<host>:30050
# Writes docs/kryton-e2e-ledger-<timestamp>.md (or LEDGER=…)
Sequential golden → bootstrap → create → delete for every catalog image. Wall time is long (hours); Microsoft media/licensing remains operator responsibility.