kubevirt-vm-migration-platform

Stage 1H: KubeVirt destination feasibility and network/storage architecture

Field Value
Project Project 1.5: VM-to-Kubernetes Migration Platform
Stage 1H
Date 2026-09-28 (all times UTC; H1 checks at 04:22)
Approval Explicitly approved by the user as design, feasibility and architecture only: research current KubeVirt, CDI, Kubernetes and AWS requirements; compare EKS with self-managed Kubernetes; design the network, storage, guest-remediation and guest-agent model; write ADRs and diagrams; make one evidence-based target-platform decision. Not approved: any AWS resource, EC2 instance, EKS or kubeadm cluster, Kubernetes/KubeVirt/CDI installation, Multus, NetworkAttachmentDefinition, StorageClass, PVC, DataVolume or VM; any change to legacy-source-vm, the Stage 1E golden artifact or the Stage 1G images; Stage 1I.
Result Success (H1 to H10 PASS). Chosen target for the first KubeVirt migration: a single-node, self-managed kubeadm cluster on one EC2 instance with nested virtualization (m8i.xlarge, ap-south-1), running CentOS Stream 9 + CRI-O 1.36 + Kubernetes 1.36 + KubeVirt v1.9.0 + CDI v1.66.1 on x86_64 with KVM. The VM uses the pod network with masquerade binding (no Multus) and gets its address by DHCP; 192.168.50.31 does not survive and is replaced by a Kubernetes Service. Storage is EBS gp3 through the EBS CSI driver, Block volume mode, ReadWriteOnce; the disk travels as qcow2 and CDI stores it as raw on the PVC. qemu-guest-agent is installed offline at conversion time, not at first boot. Nothing was created, installed or migrated.
Related ADR 007 (platform), ADR 008 (network), ADR 009 (storage and disk format), ADR 010 (guest network remediation), Stage 1G record, Stage 0 feasibility, Stage 1 index
Post-review corrections 2026-09-30. The Stage 1H architecture review (verdict: REQUIRES ARCHITECTURAL CORRECTIONS) required eight documentation corrections, review R1 to R8. With the user’s approval they are applied to this record and to ADRs 008, 009 and 010 as documentation only. The details are in section 29. No AWS action, cluster, conversion, boot or other runtime experiment was performed for them. The 2026-09-28 findings, gates H1 to H10 and sources E1 to E49 are kept as recorded. Statements changed by the review are marked “(corrected 2026-09-30, review Rn)” or “(added 2026-09-30, review Rn)”. Review R1 to R8 are the review’s correction numbers and are unrelated to the risk IDs R1 to R13 in section 27.
Later outcome Note added 2026-10-15. This record is kept as written. Statements such as “nothing was created”, “not started” and “BLOCKED” describe 2026-09-28 to 2026-09-30. On 2026-10-01, Stage 1I built this design, migrated legacy-source-vm to KubeVirt, validated it and destroyed the target. AWS credentials (U15) were working by then. Design-versus-build differences are in the “Post-implementation validation” sections of ADRs 007 to 010.

Labels: OBSERVED = seen in this lab by a command we ran. DOCUMENTED = stated by an official source listed in Sources (retrieved 2026-09-28, or 2026-09-30 where stated). INFERRED = reasoned from observations or documentation, not tested. NOT TESTED = deliberately not done. Decision states: DECIDED, DEFERRED, UNKNOWN, BLOCKED. Section 29 uses the review’s four labels:

Source references such as [E5] point to the numbered table in Sources, which records URL, title, retrieval date, the exact requirement and its implication.

Diagrams: stage-1h-target-platform.svg, stage-1h-migration-data-path.svg, stage-1h-network-model.svg.

1. Objective

Answer fifteen design questions with evidence, so that a later, separately approved stage can build the target without guessing:

# Question Answer State
1 Kubernetes distribution / environment Upstream Kubernetes bootstrapped with kubeadm, one node (control plane and worker together), on one EC2 instance DECIDED
2 Kubernetes version 1.36 (latest patch at build time; v1.36.5 was current on 2026-09-28 [E25]) DECIDED
3 KubeVirt version v1.9.0 [E1, E2] DECIDED
4 CDI version v1.66.1 [E13] DECIDED (no official KubeVirt-to-CDI matrix exists; section 8)
5 Worker node host OS CentOS Stream 9 x86_64 (EL9 kernel, aligned with the virt-launcher userland) [E6, E7, E31] DECIDED
6 Node virtualization capability EC2 nested virtualization (Intel VT-x passed through by Nitro) on m8i.xlarge; KVM (kvm_intel) in the node kernel [E32] DECIDED (to be proven on the node)
7 EKS vs self-managed vs local vs other Self-managed on EC2. EKS deferred, local VMware-hosted rejected as the primary target, bare metal only a documented fallback (sections 10 to 12) DECIDED
8 Storage model for the first cold migration EBS CSI driver, gp3, volumeMode: Block, ReadWriteOnce, 40 GiB, one AZ; CDI upload DataVolume [E14, E40] DECIDED
9 Network model Pod network with masquerade binding; flannel CNI; no Multus [E20] DECIDED
10 Can the VM retain 192.168.50.31? No (section 14) DECIDED
11 What replaces IP continuity? Service continuity: a Kubernetes Service in front of the VM, plus workload-identity checks (page hash) instead of address identity DECIDED
12 nginx validation after migration The same 12 checks as Stages 1C to 1G, run inside the guest and from outside the cluster through the Service (section 21) DECIDED
13 Guest network remediation netplan: match the NIC by driver virtio_net, dhcp4: true; applied offline to a disposable converted copy (section 15, ADR 010) DECIDED
14 Guest-agent strategy Pre-install qemu-guest-agent offline during conversion; remove the virt-v2v first-boot install; agent is useful but not a pass/fail requirement (section 19) DECIDED
15 What stays deferred Live migration, RWX storage, Multus, bridge and passt bindings, IP preservation, DNS names, HA, EKS comparison, Secure Boot, production design (section 25) DEFERRED

2. Scope

In scope, all done as desk research and design:

3. Explicit non-goals

Not done, by instruction: creating any AWS resource (VPC, EC2, EBS, IAM, EKS); installing Kubernetes, KubeVirt, CDI, Multus, a CNI or a CSI driver; creating NetworkAttachmentDefinitions, StorageClasses, PVCs, DataVolumes or VMs; running virt-host-validate; changing, shutting down or snapshotting legacy-source-vm; touching the golden artifact or the Stage 1G images; installing qemu-guest-agent anywhere; creating credentials or SSH keys; starting Stage 1I.

Also not done: no AWS CLI call was made (the CLI is not installed, and the AWS credential volume was not touched), and no Kubernetes API was contacted. Two pre-existing kubeconfig contexts on the workstation (ckad-lab, docker-desktop) belong to unrelated work and were only listed by name.

4. Current source workload

OBSERVED at H1 (04:22), unchanged since Stage 1C:

Item Value
VM legacy-source-vm, Vmid 2 on esxi-8-lab, powered on, 0 snapshots
Guest Ubuntu 24.04.5 LTS, kernel 6.8.0-142-generic (Stage 1G), UEFI, 2 vCPU / 4096 MB, 40 GiB disk (PVSCSI), one VMXNET3 NIC
Network Static 192.168.50.31/24 on VMnet8 (a VMware Workstation NAT network on the laptop), gateway and DNS 192.168.50.2, MAC 00:0c:29:0f:3d:15
Application nginx on :80; HTTP 200, 267 bytes, page sha256 b9826e18a06a354d6ba97e3419e266b1453cb2a3b0038d4b3dcd45c96c170046 from Windows and from ESXi
Other VMs conversion-host-01 (Vmid 3) on at .32; kvm-learning-01 (Vmid 1) off

5. Current converted workload findings

From the Stage 1G record, which this stage relies on and does not repeat:

Stage 1H consequences: the KubeVirt design must supply EFI (Secure Boot off), a virtio disk bus and a virtio NIC; the netplan remediation has to fit the chosen network binding; and the guest-agent install must not depend on first-boot internet access.

6. Current KubeVirt compatibility

Fact Evidence State
Newest stable release v1.9.0, published 2026-07-30 (a promotion of v1.9.0-rc.2) [E1]; the project’s stable.txt returns v1.9.0 [E2] DOCUMENTED
Newer releases Only v1.10.0-alpha.0, marked pre-release [E1]. Not used. DOCUMENTED
Kubernetes range “KubeVirt v1.9 is built for Kubernetes v1.36 and additionally supported for the previous two versions” [E3]; the support matrix lists 1.36, 1.35, 1.34 for v1.9 and no 1.37 [E4]. The user-supplied statement is confirmed. DOCUMENTED
Upstream CI on release-1.9 Presubmit lanes run on k8s-1.33 to k8s-1.36, including sig-compute, sig-network and sig-storage on k8s-1.36 [E8] DOCUMENTED
CI node platform for 1.36 kubevirtci k8s/1.36: Kubernetes 1.36.5, base centos9, CRI-O 1.36, flannel, kubeadm, systemd cgroup driver, container-selinux [E9] DOCUMENTED
virt-launcher userland Built on CentOS Stream 9 by default: libvirt 11.10.0-12.el9, QEMU 10.1.0-20.el9 [E6] DOCUMENTED
Beta features From v1.9 all Beta feature gates are on by default [E11]; v1.9.0 Beta gates include PasstBinding and Snapshot [E10] DOCUMENTED
cgroup v1 Deprecated in v1.9.0, removal planned for the next release [E12] DOCUMENTED
Architectures Operator install supports x86_64 and Arm64 [E5] DOCUMENTED

7. Kubernetes compatibility

Fact Evidence Implication
Maintained upstream minors 1.37, 1.36, 1.35 [E25] 1.37 is out (1.37.0 on 2026-08-26) but not supported by KubeVirt v1.9 [E4]
Current patches stable-1.36.txt = v1.36.5, stable.txt = v1.37.1 [E25] Pin the 1.36 patch at build time
End of life 1.36: 2027-06-28; 1.35: 2027-02-28; 1.34: 2026-10-27 [E25] 1.34 expires in a month: rejected. 1.36 has the longest window and is the version KubeVirt v1.9 is built for
kubeadm packages Per-minor repositories on pkgs.k8s.io; generic Red Hat instructions (dnf, exclude= pinning) [E26] CentOS Stream 9 is a supported install path
Privileged API server kubeadm writes --allow-privileged=true into the kube-apiserver manifest by default [E48] KubeVirt’s requirement [E5] is met by default and is inspectable
Single node Remove the control-plane taint for a single-machine cluster [E27] VMs can schedule on the only node
cgroups cgroup v1 deprecated since Kubernetes 1.35; kubelet does not start on cgroup v1 by default; RHEL 9-like distributions use v2 [E28] CentOS Stream 9 (cgroup v2) is correct for both Kubernetes and KubeVirt

8. KubeVirt/CDI compatibility tuple

The tuple was chosen as one unit, not component by component: it is the platform KubeVirt’s own CI uses for its Kubernetes 1.36 lanes [E8, E9], moved onto an EC2 instance.

Field Value Evidence
Kubernetes 1.36 (v1.36.5 observed; pin at build) [E3, E4, E25]
KubeVirt v1.9.0 [E1, E2]
CDI v1.66.1 (2026-09-06) [E13]
Node OS CentOS Stream 9, x86_64, EL9 kernel (5.14 line) [E6, E7, E30, E31]
Container runtime CRI-O 1.36.x (v1.36.6 on 2026-09-21), systemd cgroup driver, device_ownership_from_security_context = true [E5, E9, E18, E29]
Pod network (CNI) flannel (v0.28.9 current) [E9, E47]
CPU architecture x86_64 (Intel; every nested-virtualization family is Intel) [E32]
Virtualization EC2 nested virtualization, KVM as the L1 hypervisor [E32, E34]
CSI AWS EBS CSI driver (v1.66.0 current), gp3 [E40, E41]

CDI pairing evidence. No official KubeVirt-to-CDI compatibility matrix exists (also recorded in ADR 004). What does exist: CDI v1.66.0 added Kubernetes 1.36 CI lanes [E13]; KubeVirt’s CI deploys the latest CDI release (kubevirtci fetch-latest-cdi.sh, KUBEVIRT_DEPLOY_CDI=true) [E9]; KubeVirt v1.9.0 compiles against CDI API types v1.64.0, and DataVolume stays at cdi.kubevirt.io/v1beta1 [E19]. So v1.66.1 is the best-evidenced choice, and it stays a candidate until the Stage 0 runtime validation gate (feasibility section 3) passes on the real node.

9. Host requirements

What a worker node must provide, from [E5] unless noted, and how the chosen node meets it. virt-host-validate qemu is the planned check. It was not run in this stage.

Requirement Why How the chosen node meets it Planned proof (future stage)
CPU virtualization (VT-x) KVM needs it EC2 nested virtualization enabled at launch (CpuOptions NestedVirtualization=enabled) on a listed family [E32] grep -c vmx /proc/cpuinfo > 0
/dev/kvm QEMU acceleration Appears only on bare metal or with nested virtualization on; missing means it was not enabled [E34] ls -l /dev/kvm; virt-host-validate qemu
/dev/vhost-net virtio-net data path vhost_net module of the CentOS Stream 9 kernel virt-host-validate qemu
/dev/net/tun Tap devices for VM NICs tun module virt-host-validate qemu
Kernel modules kvm, kvm_intel, vhost_net, tun, plus overlay, br_netfilter for Kubernetes Standard in the EL9 kernel; loaded explicitly and persisted in /etc/modules-load.d lsmod
Privileged workloads virt-handler is a privileged DaemonSet kubeadm sets --allow-privileged=true [E48]; the kubevirt namespace gets no restrictive Pod Security label virt-handler Running
Container runtime containerd or CRI-O [E5] CRI-O 1.36 (the runtime KubeVirt CI uses [E9]) kubectl get nodes -o wide
Block device ownership CDI writes block PVCs as non-root CRI-O device_ownership_from_security_context = true [E18] CDI upload to a block PVC succeeds
Kernel/userspace alignment virt-launcher ships EL userland; divergent (especially newer non-EL) kernels are “not recommended” [E5]; upstream builds and tests only EL9 kernels [E7] CentOS Stream 9: the same distribution base and kernel line as the userland uname -r; boot a VM
AppArmor Host libvirtd profiles can block virt-handler [E5] Not applicable: CentOS Stream 9 uses SELinux, and no host libvirt is installed -
SELinux Needs container-selinux >= 2.170.0 [E5] Enforcing, with container-selinux from the distribution (as kubevirtci installs it [E9]) rpm -q container-selinux; no AVC denials
cgroups cgroup v2 required in practice [E12, E28] CentOS Stream 9 default is cgroup v2 stat -fc %T /sys/fs/cgroup = cgroup2fs
CPU architecture x86_64 or Arm64 [E5] x86_64 uname -m
Emulation fallback useEmulation: true exists for hosts without KVM [E5] Not used. If /dev/kvm is missing, the build stops instead -

10. EKS feasibility

The question was not “does EC2 support KVM” but whether an EKS worker architecture meets every row of section 9. Findings:

Aspect Finding State
Kubernetes version EKS standard support offers 1.36, 1.35, 1.34; 1.36 on EKS since 2026-06-02 [E35] Met
Privileged API server The EKS control plane is managed, so the --allow-privileged flag cannot be inspected. EKS’s default Pod Security Admission level is privileged for all namespaces [E38] INFERRED met
/dev/kvm on workers Needs the node’s launch template to set CpuOptions NestedVirtualization=enabled. The EKS list of settings prohibited in managed-node-group launch templates (subnet, IAM instance profile, shutdown/hibernate behaviour) does not include CPU options [E36]. AWS does not document EKS + nested virtualization + KubeVirt as a path. UNKNOWN until tested
Nested virtualization Same instance families as plain EC2 [E32] Available
Privileged DaemonSets Allowed by the default PSA level [E38] INFERRED met
Node OS / kernel EKS-optimized AMIs are Amazon Linux, Bottlerocket, Ubuntu and Windows [E37]; none is Enterprise Linux. KubeVirt upstream builds on CentOS Stream 9 and validates only EL9 host kernels; non-EL hosts are outside upstream CI [E7] Not aligned without a custom AMI
Kernel / module control Possible on AL2023 and Ubuntu, restricted on Bottlerocket; a custom EL9 node AMI would be self-built and self-bootstrapped Possible, at high effort
Networking Amazon VPC CNI gives each pod a VPC address from an ENI [E39]. Masquerade works with any CNI (INFERRED), but VPC CNI is not a KubeVirt CI platform Workable, untested upstream
Storage EBS CSI driver available (as an add-on or self-installed) [E40] Met
Multus Not needed by the chosen network model (section 13) N/A
KubeVirt / CDI install Operator manifests; nothing EKS-specific documented INFERRED workable
Cost Adds USD 0.10 per cluster-hour in ap-south-1 [E44], about 42% on top of the single node (section 22) Higher

Conclusion. EKS is not rejected: it may well run KubeVirt on nested-virtualization nodes. But it meets the kernel-alignment requirement only through a custom EL9 node image, which removes most of the benefit of a managed node group. Its /dev/kvm path is undocumented, its CNI is outside KubeVirt’s CI, and it costs more. EKS is therefore DEFERRED to a later comparison stage.

11. Self-managed Kubernetes feasibility

Aspect Finding State
Bootstrap kubeadm 1.36 from pkgs.k8s.io, Red Hat instructions [E26]; single-node taint removal [E27] Met
Node OS CentOS Stream 9 official AMI, x86_64, ap-south-1 ami-0e7930d02f47291cb on 2026-09-28, login user ec2-user [E31]; EOL 2027-05-31 [E30] Met (re-resolve the AMI at build)
Kernel control Full: the kernel is EL9, matching virt-launcher [E6, E7]; kernel updates are controlled by us Met
KVM availability Nested virtualization at launch on m8i.xlarge [E32] Met on paper; prove at build
Privileged workloads kubeadm default --allow-privileged=true [E48] Met
Runtime CRI-O 1.36 from the isv:cri-o stable v1.36 repository [E29] Met
Networking flannel, as in KubeVirt CI [E9]; pod network plus masquerade needs nothing else Met
Storage EBS CSI driver self-installed, IAM through an instance profile (no static keys) [E40] Met
Observability kubectl, virtctl, node journal, VMI status; no monitoring stack needed for the first migration Sufficient
Teardown Two ownership domains (corrected 2026-09-30, review R1). Terraform-managed AWS infrastructure (VPC, subnet, internet gateway, route table, security group, instance profile, the instance and its root volume) is destroyed through the Terraform state. EBS volumes created by the EBS CSI driver (the VM disk, the CDI scratch volume) are not in the Terraform state; they are deleted through Kubernetes before the destroy and checked for afterwards. Ordered sequence: section 29.1 Simple, but ordered
Terraform suitability All resources are plain EC2/VPC/IAM resources; nested virtualization is a launch CPU option Suitable (ADR 002 requires Terraform)

Conclusion. Self-managed kubeadm on EC2 meets every requirement in section 9 that can be settled on paper. It reproduces the exact platform KubeVirt tests on, and its only unproven element, /dev/kvm through nested virtualization, is shared with EKS.

12. Target platform decision

Recorded in ADR 007.

Chosen target for the first KubeVirt migration: one EC2 instance, m8i.xlarge (4 vCPU, 16 GiB, Intel, nested virtualization enabled), in ap-south-1, single AZ, running CentOS Stream 9 with a single-node kubeadm Kubernetes 1.36 cluster, CRI-O 1.36, flannel, the EBS CSI driver, KubeVirt v1.9.0 and CDI v1.66.1. Built and destroyed with Terraform (ADR 002). Not a claim about production suitability.

Alternative Outcome Reason
EKS with nested-virtualization nodes DEFERRED Section 10: kernel alignment needs a custom AMI; /dev/kvm path undocumented; +USD 0.10/h
Local VMware-hosted Kubernetes Rejected as the primary target See below
Bare-metal EC2 (m7i.metal-24xl, USD 5.0904/h in ap-south-1 [E42]) Fallback only About 23 times the chosen instance’s cost. Used only if nested virtualization fails, and only after a new decision
m7i.xlarge (USD 0.2121/h) Alternate instance Same shape and also listed [E32]; used if m8i is unavailable. 8th-generation Intel has had nested virtualization since the February 2026 launch; 7th generation since June [E33]
Multi-node cluster DEFERRED Nothing in a single cold migration needs a second node; live migration is deferred

Why local VMware-hosted Kubernetes is not the primary target:

13. Network architecture

Analysis of every current binding [E20, E21, E22, E10]. “Static guest IP” means the guest could keep a self-configured address; “reachable” is a separate property.

Property Pod network + masquerade Pod network + bridge Multus secondary + bridge Pod network + passt (core passtBinding)
Guest IP allocation DHCP from virt-launcher: 10.0.2.2/24 by default, gateway 10.0.2.1 (from vmNetworkCIDR default 10.0.2.0/24) The pod IP is delegated to the guest by DHCP; the pod keeps no IP From the NAD’s IPAM, or static in the guest if the NAD has no IPAM DHCP from passt; the guest gets the pod’s address (INFERRED from the documented IP-sync behaviour)
NAT Yes, nftables SNAT/DNAT in the pod, to and from the pod IP No No Yes, userspace L2-to-L4 translation
External reachability Through a Service to the pod IP, then DNAT to the guest Pod IP (CNI-dependent) Whatever the L2 network reaches Through a Service
Static guest IP No (DHCP expected) No (must match the pod IP) Possible No
MAC control macAddress settable (guest-side only) Settable, but some CNIs reject custom MACs Passed to the CNI; bridge and OVS support it natively -
Live migration Allowed; reach the VM through a Service because the pod IP changes Not allowed on the pod network Depends on the network; not assumed Supported; guest keeps its old IP until it renews
CNI dependency Any CNI CNI must tolerate the MAC move; Istio-type tools break Needs a CNI plugin for the secondary L2 (bridge, OVS; not macvlan/ipvlan) Any CNI
Multus dependency None None Required, plus NetworkAttachmentDefinitions None
AWS suitability Good: VM traffic leaves as the pod/node address, which the VPC accepts Workable, but the VM gets a pod IP anyway Poor: a VPC only accepts addresses assigned to an ENI from the subnet range, and source/destination checks drop others [E46]. A guest-chosen address such as .31 is not deliverable Good
Suits first cold migration Yes Weak (no benefit over masquerade, loses live migration) No (needs Multus, NADs, an L2 design and ENI IP management) Possible, but a Beta feature with 250 Mi extra memory per VM
Complexity Low Low to medium High Medium

Decision (ADR 008): pod network + masquerade, one interface, model: virtio, with an explicit port list (80, 22). No Multus is installed because this model does not need it.

The two ports serve different purposes (clarified 2026-09-30, review R4):

See section 29.4.

What 10.0.2.0/24 is, and what it is not (clarified 2026-09-30):

Traffic paths (INFERRED from [E20, E21]; to be observed on the node):

14. Network IP continuity decision

192.168.50.31 cannot survive the migration, and the design does not try to keep it. Four independent reasons:

  1. Different network. .31 lives on VMnet8, a NAT network inside VMware Workstation on the laptop. Nothing in AWS can route to it or extend it.
  2. The binding assigns the address. Masquerade hands the guest 10.0.2.2 by DHCP; a static .31 in the guest would not match the pod’s NAT and would be unreachable [E20, E21].
  3. The VPC rejects foreign addresses. An ENI carries only addresses from its subnet range, and source/destination checking drops traffic for other addresses [E46]. Carrying .31 would need a purpose-built 192.168.50.0/24 VPC, secondary ENI addresses and Multus bridging: all artifice, and still not the same network.
  4. The source keeps running. The first migration is a cold copy with the source left on as the reference and rollback. The same address on both would be a conflict, even in theory. More precisely (added 2026-09-30, review R8), it is a cold, point-in-time migration of the Stage 1E copy, with no cutover (section 29.8).

What replaces IP continuity:

Continuity type Mechanism
Service address A Kubernetes Service (ClusterIP inside the cluster, NodePort 30080 for the validation client) survives VM restarts and pod IP changes [E20]
Client-facing name DEFERRED: no DNS name in the first migration. The validation client uses node-public-IP:30080
Workload identity Proven by content, not by address: filesystem UUIDs, nginx package/config hashes, HTTP 200, page sha256 b9826e18...0046. The guest is unchanged except for the enumerated migration and preparation changes (corrected 2026-09-30, review R2; section 29.2)
Guest address Changes from 192.168.50.31 (static) to 10.0.2.2 (DHCP, inside the pod). Recorded as an infrastructure identity change

15. Guest network remediation design

Recorded in ADR 010. Requirements: no dependency on VMXNET3 or the VMware interface name, suitable for VirtIO, persistent, and consistent with masquerade (DHCP).

DESIGN ONLY. Not applied to any disk in Stage 1H. Target content of /etc/netplan/50-cloud-init.yaml (owner root:root, mode 0600, the file the guest already uses):

# DESIGN (Stage 1H) - KubeVirt target network config for legacy-source-vm.
# Match by driver so the config does not depend on the interface name or MAC.
# Address, route and DNS come from the KubeVirt masquerade DHCP server.
network:
  version: 2
  ethernets:
    primary:
      match:
        driver: virtio_net
      dhcp4: true
      dhcp6: false
Property How the design meets it
VMXNET3 independence No vmxnet3 or ens192 reference; the match is the driver the target provides
VirtIO suitability virtio_net is the driver of KubeVirt’s model: virtio NIC; Stage 1G proved driver matching works ([Match] Driver=virtio_net)
Persistence A normal netplan file in the guest image, not a runtime tweak
No VMware naming No name match, no set-name, no MAC match (the MAC changes unless set)
Chosen network model DHCP, as masquerade expects [E20]

How and where it is applied (a future, separately approved stage): offline with guestfish or virt-customize, on a new disposable copy of the virt-v2v output, on conversion-host-01. Never on the golden artifact, the working copy or the kept Stage 1G images. The prepared image must pass the pre-transfer boot test on conversion-host-01 before it leaves the lab (added 2026-09-30, review R5; section 29.5). Known limits: with more than one virtio NIC the match would configure all of them (fine for the single-NIC design). The inert ens192 in /etc/cloud/cloud.cfg.d/90-installer-network.cfg is left alone because cloud-init is disabled.

16. Storage architecture

Recorded in ADR 009.

Item Decision Evidence
Provisioner AWS EBS CSI driver (ebs.csi.aws.com), installed by us on the self-managed cluster; IAM through the instance profile with AmazonEBSCSIDriverPolicyV2; IMDSv2 with hop limit 2 so the driver pods can reach instance metadata [E40]
Volume type gp3: 3,000 IOPS and 125 MiB/s baseline included, 1 GiB to 64 TiB [E41]
StorageClass (design) type: gp3, encrypted: "true" (added 2026-09-30, review R7), volumeBindingMode: WaitForFirstConsumer, reclaimPolicy: Delete, allowVolumeExpansion: true INFERRED design; encrypted defaults to false in the driver [E50]
Encryption at rest Every CSI-created volume (VM disk and CDI scratch) is encrypted through the StorageClass parameter, not through an assumed default (added 2026-09-30, review R7; section 29.7) [E50]
Volume mode Block for the VM disk: KubeVirt consumes the raw device directly, with no disk.img file or filesystem overhead [E23] [E23, E40]
Scratch space CDI always requests scratch as Filesystem, ReadWriteOnce, and upload always needs scratch; the same gp3 class serves both modes [E17]
Access mode ReadWriteOnce. EBS is RWO and AZ-scoped; the VM is therefore not live-migratable, which is accepted for a cold migration [E22], ADR 002
Disk bus virtio (proven in Stage 1G; KubeVirt’s standard bus) [E23]
Size 40 GiB, exactly the source’s virtual size (42,949,672,960 bytes). Never smaller. Keeping it equal avoids CDI growing the virtual disk on import [E15] Stage 1G
Import mechanism CDI upload DataVolume (source: upload), filled by virtctl image-upload [E14, E16]
Lifecycle A standalone DataVolume, not a dataVolumeTemplate. CDI creates the PVC for it and manages it through the DataVolume [E55]; the VM only references that PVC, so deleting the VM does not delete the migrated disk. Deleting the disk is therefore a deliberate teardown step: delete the DataVolume, then verify that the PVC, the PV and the EBS volume are gone. The EBS volume is outside the Terraform state (added 2026-09-30, review R1; clarified 2026-09-30; section 29.1) [E23, E55]
CRI setting device_ownership_from_security_context = true in CRI-O, required for CDI on block PVCs [E18]

Cold versus live migration. Live migration needs a shared ReadWriteMany volume and rules out pod-network bridge binding [E22]. The first migration is a cold copy of a powered-off disk into a new VM, so it needs neither shared storage nor live migration. The design is deliberately not shaped around live migration. RWX storage (EFS, or a clustered block solution) is DEFERRED.

17. Disk-format decision

Stage of the path Format Why
Input (golden, read only) VMDK: vmfs descriptor + 40 GiB flat extent What cold acquisition produced (Stage 1E). Never converted in place
Conversion output / transfer format qcow2 (virt-v2v -of qcow2, then remediation and agent install on a copy) Guest-aware conversion is required (netplan, VMware Tools, first-boot jobs). qcow2 carries only allocated data: about 2.7 GiB to move instead of 40 GiB. CDI accepts qcow2 for upload and import [E14]
Target representation raw on a Block-mode PVC CDI converts every supported format to raw [E14, E15]; KubeVirt reads raw block devices directly [E23]

What “raw at rest” means here (clarified 2026-09-30):

The uploaded qcow2 must be standalone (added 2026-09-30):

Not chosen:

18. CDI workflow

Design, in order. Nothing was run.

  1. Prepare the image (future stage, on conversion-host-01):
    • Copy the kept virt-v2v output.
    • Apply the section 15 netplan design and the section 19 agent install to the copy.
    • Run qemu-img check, sha256 the result and make it read-only.
    • Confirm the result is a standalone qcow2 (section 17).
    • Write the preparation record of section 29.2 (added 2026-09-30, review R2).
  2. Pre-transfer boot test (added 2026-09-30, review R5): a mandatory gate. Boot the exact prepared qcow2 once on conversion-host-01 as defined in section 29.5, and transfer nothing until it passes.
  3. Transfer: move the qcow2 (about 2.7 GiB) to the node over SSH and verify the sha256 on both ends.
    • The route (directly from the conversion host, or through Windows with only about 5.8 GiB free) is a Stage 1I detail (DEFERRED).
    • Any route must meet the mandatory constraints of section 29.6 (added 2026-09-30, review R6).
    • Inbound transfer into AWS carries no data-transfer charge (INFERRED from AWS pricing practice; not priced here).
  4. Upload on the node, so the upload proxy is never exposed: kubectl port-forward -n cdi service/cdi-uploadproxy 8443:443 [E16], then virtctl image-upload dv legacy-source-vm-disk --size=40Gi --volume-mode=block --access-mode=ReadWriteOnce --storage-class=<gp3 class> --image-path=<qcow2> --uploadproxy-url=https://127.0.0.1:8443 --insecure --force-bind (flags from the v1.9.0 source [E49]; --force-bind avoids waiting for a consumer under WaitForFirstConsumer).
  5. CDI processing: an upload server receives the qcow2 into Filesystem scratch space, qemu-img converts it to raw onto the Block PVC, and the scratch PVC is removed [E17]. Confirm that the scratch PVC is actually gone (added 2026-09-30, review R1). If a scratch volume is created, record its EBS volume ID from its PV while it exists (clarified 2026-09-30; section 29.1).
  6. Verify: DataVolume phase Succeeded, PVC Bound, size 40Gi, volumeMode: Block. Record the EBS volume ID of the migrated disk from its PV (clarified 2026-09-30; section 29.1).
    • Succeeded proves that CDI finished writing. It does not prove that the right image was imported, or that the guest workload is correct (clarified 2026-09-30).
    • That proof comes from two further checks:
      • the artifact check before upload: the file’s sha256 equals the preparation record;
      • the guest-level checks of section 21, run after boot.
  7. VM: a VirtualMachine referencing the PVC with bus: virtio, EFI with secureBoot: false, 2 vCPU / 4096 Mi (from the .vmx, not the virt-v2v defaults), one masquerade interface, runStrategy: Manual so start and stop are explicit.
    • Machine type, CPU model and EFI variable-store persistence are left at KubeVirt’s defaults.
    • The values actually applied are recorded at runtime, not treated as fixed design values (clarified 2026-09-30).
    • Disk lifecycle (clarified 2026-09-30). The chain is: VirtualMachine -> references the standalone PVC -> the PVC is managed by the standalone DataVolume from step 4 -> the EBS volume behind its PV. The VM does not own the migrated disk’s lifecycle; the standalone DataVolume is the lifecycle object for the migration disk. CDI’s completed-DataVolume garbage-collection setting was removed in v1.62 [E56], so the DataVolume is expected to remain after Succeeded (ENGINEERING INFERENCE; to be observed). The disk is removed only by deliberately deleting the DataVolume in teardown, followed by verification that the PVC, the PV and the EBS volume are gone (section 29.1).
  8. Runtime: virt-launcher pod -> libvirt -> QEMU with /dev/kvm -> Ubuntu -> nginx.
  9. Evidence and teardown (added 2026-09-30, review R1): capture the evidence, delete the qcow2 from the node, and tear down in the order of section 29.1.

19. Guest-agent strategy

Question Answer
Required for boot? No. KubeVirt calls the guest agent “an optional component” [E24]; Stage 1G booted without it
What it adds The VMI condition AgentConnected, guest OS info and guest-reported interfaces in status, and the guestosinfo, userlist and filesystemlist subresources [E24]
Needed for the first migration? No. It is an extra, non-blocking observation. The pass/fail checks (section 21) do not depend on it
Offline install without internet on first boot? Yes, by design: install it before import
Install during conversion? Yes: on a disposable copy of the virt-v2v output. Fetch qemu-guest-agent and any missing dependencies as .deb files on conversion-host-01 (the same Ubuntu 24.04 release) with apt-get download, upload them into the image, and run dpkg -i offline with virt-customize (guestfs-tools 1.52.0 is installed, ADR 006). Then delete the virt-v2v guestfs-firstboot scripts that run apt-get update/install and start the agent, so first boot never touches the network for packages. Refined 2026-09-30 (review R2): see the notes below this table
Install offline before import? Yes, that is the same step: the image leaves the conversion host with the agent installed and no pending first-boot package work
How KubeVirt detects and uses it Through the QEMU guest-agent virtio-serial channel (virt-v2v’s own XML also declares org.qemu.guest_agent.0); presence is reported as AgentConnected [E24]. That the channel is added automatically is INFERRED

Rule kept: no agent installation depends on uncontrolled internet access at first boot, and nothing is installed into the source VM or the kept Stage 1G images.

Refinements to the install step (added 2026-09-30, review R2):

20. First-migration architecture

End to end (see stage-1h-migration-data-path.svg and stage-1h-target-platform.svg):

ESXi legacy-source-vm -> cold acquisition (Stage 1E, done) -> golden VMDK on Windows (read only) -> working copy on conversion-host-01 (immutable) -> virt-v2v guest-aware conversion to qcow2 (Stage 1G, done) -> disposable copy with netplan remediation and offline guest agent (future) -> pre-transfer boot test on conversion-host-01 (future gate, section 29.5) -> transfer to the EC2 node -> CDI upload DataVolume -> raw Block PVC on EBS gp3 -> VirtualMachine -> virt-launcher pod -> QEMU/KVM on /dev/kvm (nested virtualization) -> Ubuntu 24.04 -> nginx.

Network path (see stage-1h-network-model.svg): guest virtio NIC (DHCP 10.0.2.2) -> masquerade binding in the virt-launcher pod (NAT here) -> pod network (flannel) -> node ENI (VPC private IP) -> public IPv4. The original VMware IP 192.168.50.31 stays on VMnet8 with the running source. IP continuity: none, by design. DNS: cluster DNS for the guest (INFERRED), no public name (DEFERRED). External HTTP validation point: node public IPv4, NodePort 30080, from the operator /32.

Migration semantics (added 2026-09-30, review R8):

21. Application validation model

The same 12 checks used since Stage 1C, adapted to KubeVirt. Access (corrected 2026-09-30, review R4):

There is no guest SSH Service: NodePort 30080 is HTTP validation only (section 29.4).

# Check How on KubeVirt Pass condition
1 Boot VMI phase Running; console shows a login prompt Reaches multi-user target
2 Mounts findmnt /, findmnt /boot/efi Mounted by UUID, as the baseline
3 OS identity /etc/os-release, uname -r Ubuntu 24.04.5, kernel 6.8.0-142-generic
4 Filesystem UUIDs blkid Equal to the Stage 1C baseline
5 Application files sha256 of the page and nginx config Equal to the baseline
6 nginx package dpkg -s nginx Same version
7 nginx config nginx -t, config hash Valid, unchanged
8 nginx service systemctl is-active nginx active
9 HTTP 200 curl guest-local, then through the Service 200
10 Page hash sha256 of the response body b9826e18...0046
11 Application content Page marker p15-stage1c-source-v1 Present
12 Network reachability From outside: operator -> node-public-IP:30080; from the guest: gateway 10.0.2.1, DNS lookup 200 from outside; gateway reachable

Extra, non-blocking observations: AgentConnected, guestosinfo, systemctl is-system-running = running (no pending first boot).

Infrastructure identity (expected to change, recorded not failed): hypervisor and Vmid (ESXi Vmid 2 -> KubeVirt VMI UID), MAC (00:0c:29:0f:3d:15 -> KubeVirt-generated), disk (PVSCSI sda -> virtio vda, VMDK -> raw PVC), interface name (ens192 -> a virtio name), SMBIOS/firmware IDs and NVRAM, IP (192.168.50.31 -> 10.0.2.2 behind the pod IP).

Workload identity: unchanged except for the explicitly enumerated migration and preparation changes (corrected 2026-09-30, review R2).

The original 2026-09-28 wording, which listed the Ubuntu root filesystem content among the things that stay the same, contradicted the design’s own guest changes and is superseded.

22. AWS resource model

Designed, not provisioned. Region ap-south-1 (Asia Pacific, Mumbai), on-demand Linux, public price files retrieved 2026-09-28 ([E42] published 2026-09-25, [E43] 2026-09-25, [E44] 2026-09-18, [E45] 2026-09-17).

Resource Design
Network One VPC, one public subnet in one AZ, internet gateway, route table. No NAT gateway. Proposed ranges: VPC 10.40.0.0/16, subnet 10.40.1.0/24 (added 2026-09-30, review R3; address plan in section 29.3)
Security group Inbound only from the operator’s /32: TCP 22 (SSH to the node’s own sshd, not to the guest) and TCP 30080 (HTTP validation NodePort). The API (6443) is reached through the SSH tunnel. Outbound: all. Guest port 22 is never opened here (clarified 2026-09-30, review R4)
Instance 1 x m8i.xlarge (4 vCPU, 16 GiB), CpuOptions NestedVirtualization=enabled, CentOS Stream 9 AMI, IMDSv2 required with hop limit 2
Root volume 50 GiB gp3, encrypted (added 2026-09-30, review R7) (OS, container images, the uploaded qcow2). Terraform-managed
VM disk 40 GiB gp3 (dynamic, from the EBS CSI driver), plus a temporary scratch volume of about 40 GiB during import. Both encrypted through the StorageClass (review R7). Both outside the Terraform state (review R1)
IAM Instance profile with AmazonEBSCSIDriverPolicyV2 only [E40]
Public IPv4 One (auto-assigned)
Lifecycle Terraform apply for a session, destroy afterwards (ADR 002), in the teardown order of section 29.1, which deletes the CSI-created volumes first and checks for leftovers after the destroy (corrected 2026-09-30, review R1)

Minimum footprint: 1 node, 4 vCPU, 16 GiB RAM, 90 GiB of gp3 steady state (plus about 40 GiB for the length of the import), nested virtualization (no extra charge [E32]), 1 public IPv4.

Cost, calculated only from the stated assumptions (730 hours per month; gp3 at list price with baseline performance; data transfer not priced, since it is expected to be negligible: inbound image and package traffic plus KB-sized HTTP responses):

Item Unit price Per hour 730 h
m8i.xlarge USD 0.2227/h 0.2227 162.57
gp3 root 50 GiB USD 0.0912/GB-month 0.0062 4.56
gp3 VM disk 40 GiB USD 0.0912/GB-month 0.0050 3.65
Public IPv4 USD 0.005/h 0.0050 3.65
Total, self-managed   about 0.239 about 174.43
EKS control plane (if EKS were used) USD 0.10/h +0.1000 +73.00
Bare-metal fallback m7i.metal-24xl instead of m8i.xlarge USD 5.0904/h +4.8677 -

A 4-hour working session costs about USD 0.96, plus the scratch volume for under an hour (about USD 0.005). Left running for a month, it would cost about USD 174, which is why teardown is part of the design.

23. Security model

Area Design
Privileged KubeVirt workloads virt-handler and parts of CDI run privileged; the whole node is therefore trusted infrastructure. No other workloads, no multi-tenancy
Node isolation One disposable node in its own VPC; nothing else in the account depends on it
SSH A new, dedicated key pair created at build time on the workstation, never committed. Security group limited to the operator /32. Password login off (AMI default)
AWS credentials None in the repository, manifests or the node. Terraform runs with the operator’s own credentials (still BLOCKED, see section 26). The node uses its instance profile only. The Docker volume platform-aws-tools-aws is untouched
Instance metadata IMDSv2 only; hop limit 2 is needed by the EBS CSI driver [E40], which also lets any pod read the instance role. Mitigation: the role holds only the EBS CSI policy, and the node runs only our workloads
RBAC kubeadm admin kubeconfig kept on the workstation outside Git; no extra users. KubeVirt’s default roles unchanged
Network exposure Only 22 (node sshd) and 30080 (HTTP validation) from one /32. The upload proxy is used through port-forward on the node and never exposed. Guest management goes through the Kubernetes API, never through a Service (clarified 2026-09-30, review R4)
Storage permissions The EBS CSI policy is scoped to volumes tagged ebs.csi.aws.com/cluster: true [E40]. Corrected 2026-09-30 (review R1): the original text said that reclaimPolicy: Delete removes the volumes together with the cluster, which is inaccurate. reclaimPolicy: Delete acts only when a PVC is deleted while the CSI driver still runs. Terminating the instance leaves CSI-created volumes behind as billed, available volumes, outside the Terraform state. They are deleted through their DataVolume before the destroy and verified gone by their recorded volume IDs. After the destroy, that tag is used for detection only: an unrecognized volume is never deleted automatically, because the tag is not unique to this project (clarified 2026-09-30; section 29.1)
Encryption at rest Root volume and all CSI-created volumes encrypted (added 2026-09-30, review R7; section 29.7)
Disk artifacts Every copy of the guest disk (VMDK, qcow2, EBS volume) is sensitive data: it contains SSH host private keys, password hashes, machine identity, logs and application data. Handling rules in section 29.7 (added 2026-09-30, review R7)
Secrets No Kubernetes Secrets with cloud credentials. Kubeconfig, SSH private key and Terraform state stay outside Git (.gitignore)
SELinux Enforcing (as in KubeVirt CI [E9]). Any denial is recorded, and the stage stops rather than silently switching to permissive
Feature gates v1.9 enables all Beta gates by default [E11]. Accepted for a disposable lab; disabling unused ones is DEFERRED hardening
Guest identity The migrated guest keeps the source’s SSH host keys and machine-id. It never shares a network with the source

24. Decisions

# Decision Value State
1 Kubernetes environment Self-managed, kubeadm, single node on EC2 DECIDED (ADR 007)
2 Kubernetes version 1.36 (patch pinned at build) DECIDED
3 KubeVirt version v1.9.0 DECIDED
4 CDI version v1.66.1 DECIDED (candidate until the runtime gate passes)
5 Node OS CentOS Stream 9 x86_64 DECIDED
6 Container runtime CRI-O 1.36 DECIDED
7 EC2 instance strategy m8i.xlarge, nested virtualization, on demand, single AZ; m7i.xlarge alternate; metal only after a new decision DECIDED
8 Storage model EBS CSI, gp3, Block, RWO, 40 GiB, standalone upload DataVolume DECIDED (ADR 009)
9 Target disk format qcow2 in transit, raw on a Block PVC DECIDED (ADR 009)
10 Network model Pod network, masquerade, flannel, no Multus DECIDED (ADR 008)
11 Guest network remediation netplan driver match virtio_net, DHCP, offline on a disposable copy DECIDED (ADR 010)
12 Guest-agent approach Offline pre-install at conversion, remove first-boot install, non-blocking DECIDED
- Pod network CNI flannel DECIDED
- Region ap-south-1 DECIDED
- SELinux mode Enforcing DECIDED
- IP continuity Not preserved; Service continuity instead DECIDED

25. Deferred decisions

Item Why deferred
Live migration and RWX storage Not needed for a cold migration; EBS is RWO
Multus, secondary networks, bridge binding Not needed by masquerade; poor fit for a VPC
passt binding Beta; no benefit for the first migration
Any form of IP preservation Rejected by design (section 14)
DNS name, Route 53, LoadBalancer, Ingress NodePort from one /32 is enough for validation
EKS comparison Section 10; a later comparison stage
Multi-node or HA cluster, multiple AZs Nothing in the first migration needs them
Bare-metal instances Only if nested virtualization fails
Secure Boot on Not proven in Stage 1G
CentOS Stream 10 nodes When KubeVirt’s default userland moves to CS10
MAC preservation, cloud-init enablement, instancetypes/preferences Not needed
Snapshots, backup, Beta-gate hardening, monitoring stack After the first migration works
The route for moving the qcow2 to AWS Stage 1I detail (section 18). Any route must meet the mandatory constraints of section 29.6 (added 2026-09-30, review R6). S3 staging is not part of this design
Terraform code, GitOps, the custom controller (ADR 005), warm migration (ADR 003) Later stages
Production design Out of scope. Not a claim about production suitability

26. Unknowns

# Unknown How it will be resolved
U1 Whether m8i.xlarge reports nested-virtualization in ap-south-1. AWS says nested virtualization is in all commercial Regions [E33], and the family is sold there [E42], but no describe-instance-types call was made Pre-flight aws ec2 describe-instance-types before any launch
U2 KubeVirt stability and performance on EC2 nested virtualization (L2 guests on Nitro) First boot; AWS itself recommends bare metal for performance-sensitive work [E32]
U3 The EKS nested-virtualization path Only if EKS is revisited
U4 container-selinux version on current CentOS Stream 9 (needs >= 2.170.0) rpm -q on the node
U5 Guest DNS through masquerade DHCP (resolver handed to the guest) Observe in the guest
U6 This guest’s EFI boot with KubeVirt’s OVMF build and fallback loader (proven only with Ubuntu’s OVMF) First boot
U7 The virtio NIC name inside the KubeVirt guest Irrelevant with the driver match; observe
U8 The offline dependency set of qemu-guest-agent for the guest’s package state apt-get download plus a dry run on the conversion host
U9 Whether virt-v2v’s first-boot scripts skip the agent install when it is already present Inspect the scripts on the copy; remove them regardless. 2026-09-30: moot for the design. The Stage 1G record shows all five scripts serve the agent install, and the design now removes all five plus the service (section 29.2)
U10 CDI upload under WaitForFirstConsumer on a single node with --force-bind First upload
U11 EBS CSI metadata access through flannel with hop limit 2 Driver logs
U12 Formal KubeVirt-to-CDI compatibility Not published; the runtime gate decides
U13 Data-transfer charges Not priced; expected to be negligible
U14 The CentOS Stream 9 AMI ID at build time Re-resolve from centos.org
U15 AWS credentials (ADR 002 blocker) BLOCKED for any build; not needed for this design stage

27. Risks

# Risk Mitigation
R1 EC2 nested virtualization is new (February 2026) and slower than metal Functional goals only; metal fallback costed; stop if /dev/kvm is missing (no emulation)
R2 CentOS Stream 9 reaches EOL on 2027-05-31 Disposable lab; plan the CS10 move with KubeVirt
R3 A kernel update on the node could diverge from the virt-launcher userland Record uname -r; do not update the kernel mid-session
R4 Single node is a single point of failure Disposable; the source of truth is the conversion artifact plus Git
R5 Pods can read the instance role through IMDS (hop limit 2) Minimal IAM policy; no other workloads
R6 Public exposure of SSH and the NodePort Operator /32 only; teardown after the session
R7 The guest boots unreachable if DHCP or the netplan design fails virtctl console/vnc for diagnosis; the design was proven in principle in Stage 1G
R8 CDI scratch space temporarily doubles storage Budgeted (section 22)
R9 Forgotten teardown leaks about USD 174 per month Session checklist; Terraform destroy. Corrected 2026-09-30 (review R1): also the post-destroy check for CSI-created volumes (section 29.1), because the destroy alone does not remove them
R10 SELinux enforcing blocks a component Record AVC, stop, decide explicitly
R11 All Beta feature gates on by default Accepted for a lab; hardening deferred
R12 Duplicate guest identity (machine-id, host keys, hostname) The source and target never share a network
R13 EBS is AZ-scoped; no live migration Accepted: cold migration only

28. Stage 1I entry criteria

Stage 1I is not defined or started here. Status at the end of Stage 1H:

Criterion Status
Target environment chosen Met: section 12, ADR 007
Compatible tuple Met on paper: section 8 (candidate until the runtime gate)
Worker requirements defined Met: section 9
Node OS chosen Met: CentOS Stream 9
Hardware virtualization path defined Met: EC2 nested virtualization, m8i.xlarge (U1 open)
Network model chosen Met: masquerade, ADR 008
Guest remediation defined Met: section 15, ADR 010
IP continuity decision Met: not preserved; Service continuity (section 14)
Storage class/model defined Met: EBS CSI gp3 Block RWO, ADR 009
Target format Met: qcow2 transfer, raw on PVC
CDI workflow Met: section 18
Guest-agent strategy Met: section 19
AWS footprint defined without provisioning Met: section 22
Source healthy Met: H1 and final check
Golden protected Met: hashes, size, timestamps and ReadOnly attribute unchanged
No Kubernetes/KubeVirt/CDI resources exist Met: nothing created
No migration occurred Met

Stage 1I also requires the user’s explicit approval, with objective, scope and change boundary written down, and working AWS credentials (U15).

Added 2026-09-30. The Stage 1H architecture review found that the “Met” statuses above were not sufficient on their own, and required corrections review R1 to R8. They are now documented in section 29:

Criterion (review) Status
Teardown and EBS lifecycle defined (R1) Defined: section 29.1 (not executed)
Guest identity and intended changes defined (R2) Defined: section 29.2; preparation evidence pending future image preparation
Address plan defined (R3) Defined as proposed design: section 29.3; nothing runtime-confirmed
Guest management path defined (R4) Defined: section 29.4
Pre-transfer boot test gate defined (R5) Defined: section 29.5 (not executed)
Transfer constraints defined (R6) Defined: section 29.6; route still deferred
Encryption and artifact handling defined (R7) Defined: section 29.7
Point-in-time, no-cutover semantics stated (R8) Stated: section 29.8

Stage 1I remains not started. Whether the corrected design is ready for approval is a human decision. Documenting these corrections does not approve Stage 1I.

Note added 2026-10-15: Stage 1I was later approved gate by gate and completed on 2026-10-01 (I1 to I5 PASS). Section 29.1 (teardown) and section 29.5 (pre-transfer boot test) were executed there. See the Stage 1I record.

29. Post-review corrections (review R1 to R8)

Added 2026-09-30 from the Stage 1H architecture review, section 15, with the user’s approval.

29.1 Review R1: teardown and EBS lifecycle

The original record said that Terraform destroys the instance “and its volumes”, and that reclaimPolicy: Delete removes the volumes with the cluster. That conflated two different ownership domains.

Domain Resources Created by Removed by Basis
Terraform-managed AWS infrastructure VPC, subnet, internet gateway, route table, security group, IAM role and instance profile, the EC2 instance and its root volume terraform apply (ADR 002) terraform destroy, which only knows what is in its state PROPOSED DESIGN (ADR 002)
Kubernetes / EBS CSI-provisioned storage The 40 GiB VM disk volume; the roughly 40 GiB CDI scratch volume (normally removed by CDI after the import) The EBS CSI driver, when a PVC is bound. It tags the volume ebs.csi.aws.com/cluster = true; the IAM policy only lets it manage volumes with that tag (or with kubernetes.io/created-for/pvc/name) [E40, E51] Deleting the standalone DataVolume, which removes its PVC, while the driver still runs (reclaimPolicy: Delete); otherwise only an explicit deletion of a volume positively identified as this session’s CURRENT UPSTREAM DOCUMENTATION [E51, E55]; ENGINEERING INFERENCE

Why this matters (ENGINEERING INFERENCE):

Session volume record (PROPOSED DESIGN; clarified 2026-09-30). During the migration session, record the exact EBS volume IDs created for:

No volume ID exists yet, and none is invented here.

Disk lifecycle (clarified 2026-09-30). The VirtualMachine references the standalone PVC, and the PVC is managed by the standalone DataVolume. The VM does not own the migrated disk’s lifecycle: the DataVolume is the lifecycle object. The normal workflow is to delete the DataVolume deliberately, then verify that the PVC and PV are gone, then verify that the underlying EBS volume is deleted. Deleting the PVC directly is not part of the normal workflow (step 3).

Teardown sequence (PROPOSED DESIGN; not executed; performed by the operator, and needs the AWS credentials that are still BLOCKED, U15):

  1. Capture validation evidence. Record, as text:
    • the 12 check results and the recorded KubeVirt runtime defaults;
    • VMI status and conditions, and events;
    • DataVolume, PVC and PV descriptions, including the recorded session volume IDs.

    Never capture disk content.

  2. Stop and delete the KubeVirt workload. Stop the VM, then delete the VirtualMachine and the validation Service. This does not delete the migrated disk, because the VM does not own it.
  3. Delete the migration disk through its DataVolume.
    • Deliberately delete the standalone DataVolume. Its PVC is removed with it, and with reclaimPolicy: Delete the EBS CSI driver then deletes the PV and the EBS volume (ENGINEERING INFERENCE from [E55] and the StorageClass design; verified in step 4).
    • If the PVC is still present after the DataVolume is gone, record that as a finding, then delete the PVC deliberately. This is an exception path, not an equivalent alternative.
  4. Verify that the PVC, the PV and the known EBS volumes are gone (mandatory before step 5):
    • the migration PVC no longer exists;
    • its PV no longer exists;
    • each recorded session volume ID (VM disk, and scratch if recorded) is reported deleted by AWS;
    • no scratch PVC or PV remains.

    Do not run the Terraform destroy until all of these hold.

  5. Run the Terraform destroy for the infrastructure.
  6. Run the post-destroy EBS check (mandatory; detection only):
    • List volumes in the account and Region tagged ebs.csi.aws.com/cluster = true or kubernetes.io/created-for/pvc/name.
    • Confirm that none of the recorded session volume IDs still exists.
    • Confirm that the root volume went with the instance.

    This step deletes nothing. Expected result: no volume belonging to this session.

  7. Handle a detected volume only after positive identification.
    • Record its ID, size, creation time and tags (no content).
    • Delete it only if it is positively identified as belonging to this migration session: its ID is one of the recorded session volume IDs, or the recorded evidence otherwise ties it unambiguously to this session. Record the basis for the identification, delete it, and confirm the deletion.
    • An unknown or unrecognized EBS volume is NOT deleted automatically, and not by this procedure at all. It may belong to another cluster in the same account and Region. Record it and stop for a human decision.
    • Never attach a volume to another instance to inspect it.
  8. Delete the temporary qcow2 from the AWS node.
    • Normally this is already done right after a successful import (section 29.6, item 9).
    • If not, do it before step 5, because after the destroy the node no longer exists. At that point this step reduces to confirming that the encrypted root volume was deleted with the instance.
  9. Retain only intentionally preserved evidence:
    • text evidence kept outside Git, or redacted per the standing repository rules;
    • no disk content left in AWS;
    • the authoritative disk copies stay in the lab (the golden VMDK, and the prepared qcow2 on conversion-host-01).

The goal is unchanged: no orphaned storage and no storage deliberately left billed. The post-destroy verification remains mandatory; it detects, and deletion is limited to volumes positively identified as this session’s.

29.2 Review R2: guest identity and intended changes

Definition: the migrated guest is unchanged except for the explicitly enumerated migration and preparation changes below. Anything else that differs is a finding.

# Intended change Introduced by Evidence status
C1 open-vm-tools purged: binaries, libraries, plugins, /etc/vmware-tools/, /etc/pam.d/vmtoolsd, units, rc links, udev rules 60-open-vm-tools.rules and 99-vmware-scsi-udev.rules virt-v2v PROJECT-DERIVED FACT (Stage 1G section 14, virt-diff)
C2 /etc/initramfs-tools/modules gains the virt-v2v comment and bochs; the initramfs for 6.8.0-142-generic is rebuilt; the old one is kept as /boot/initrd.img-6.8.0-142-generic.pre-v2v virt-v2v PROJECT-DERIVED FACT (Stage 1G section 14)
C3 New /etc/modprobe.d/virt-v2v-added.conf: alias scsi_hostadapter virtio_blk virt-v2v PROJECT-DERIVED FACT (Stage 1G section 14)
C4 /var/lib/dpkg/status rewritten by the purge. Incidental: /etc/ld.so.cache, /run/blkid, /run/needrestart virt-v2v PROJECT-DERIVED FACT (Stage 1G section 14)
C5 The first-boot mechanism that virt-v2v added is removed again: /usr/lib/systemd/system/guestfs-firstboot.service with its SysV links, /usr/lib/virt-sysprep/firstboot.sh, and its five scripts 5000-0001-wait-online, 5000-0002-setenforce-0, 5000-0003-install-qga, 5000-0004-setenforce-restore, 5000-0005-start-qga. Net effect after preparation: none of these present virt-v2v adds; preparation removes Addition: PROJECT-DERIVED FACT (Stage 1G section 14). Removal: PROPOSED DESIGN; the exact removed-file list is pending future image preparation
C6 /etc/netplan/50-cloud-init.yaml replaced with the section 15 content, owner root:root, mode 0600 Preparation (ADR 010) Content: PROPOSED DESIGN (section 15). File hash: pending future image preparation
C7 qemu-guest-agent and its exact dependency set installed: the files owned by those packages (including their systemd and udev units), plus the dpkg database and dpkg log entries the install causes Preparation (section 19) Package names, versions and hashes: pending future image preparation (U8). None are invented here

Explicitly not intended to change (PROJECT-DERIVED FACT: checksum-identical after virt-v2v in Stage 1G section 14):

Runtime state written after the guest boots (logs, journal, timestamps) is not an identity change (ENGINEERING INFERENCE).

Preparation record (PROPOSED DESIGN; written during future image preparation; no hash below exists yet, and none is invented):

29.3 Review R3: address plan

The AWS subnet is intentionally contained within the VPC: 10.40.1.0/24 lies inside 10.40.0.0/16. That containment is required, not an overlap to avoid (clarified 2026-09-30).

The independent address domains must not overlap:

The masquerade CIDR is a per-VM, guest-side network inside each virt-launcher pod, not a shared subnet (section 13).

Status meanings:

Network Value Status Basis
AWS VPC CIDR 10.40.0.0/16 Proposed; selected for the lab PROPOSED DESIGN, chosen to stay clear of every independent domain below (the subnet is carved from it)
AWS subnet CIDR (one public subnet, one AZ) 10.40.1.0/24 Proposed; selected for the lab PROPOSED DESIGN
Kubernetes Pod CIDR 10.244.0.0/16 Default (flannel v0.28.9 net-conf.json, VXLAN backend); selected for the lab. kubeadm must be given the same value CURRENT UPSTREAM DOCUMENTATION [E52]
Kubernetes Service CIDR 10.96.0.0/12 (10.96.0.0 to 10.111.255.255); cluster DNS 10.96.0.10 Default (kubeadm); selected for the lab CURRENT UPSTREAM DOCUMENTATION [E53]
KubeVirt masquerade vmNetworkCIDR 10.0.2.0/24: gateway 10.0.2.1, guest 10.0.2.2, inside each VM’s virt-launcher pod (section 13) Default; selected for the lab; unchanged from the 2026-09-28 design CURRENT UPSTREAM DOCUMENTATION [E21]
Kept clear of CRI-O’s default bridge network 10.85.0.0/16 [E54]; the lab network 192.168.50.0/24, which is not routed to AWS and is avoided so records stay unambiguous Constraint CURRENT UPSTREAM DOCUMENTATION [E54]; PROJECT-DERIVED FACT

Overlap check (ENGINEERING INFERENCE, by arithmetic):

Why it matters (ENGINEERING INFERENCE):

Future pre-flight checklist. CNI prerequisites and the pod and Service CIDR selections belong in the future implementation pre-flight checklist, which is not written in this task. That checklist includes:

29.4 Review R4: guest management path

The original access line in section 21 offered guest SSH via the Service as an alternative to virtctl ssh. That was inaccurate: the design has one Service, and it exposes HTTP on NodePort 30080 only.

Path Purpose Transport Opened in the AWS security group?
Node SSH, TCP 22 on the node’s public IPv4 Node administration, the API tunnel, the transfer target (section 29.6) SSH to the node’s own sshd Yes, from the operator /32 only
Kubernetes API, TCP 6443 kubectl, virtctl Through the node SSH tunnel No
virtctl console Serial console of the guest Kubernetes API, then KubeVirt to the virt-launcher pod No
virtctl vnc Graphical console, where applicable Kubernetes API No
virtctl ssh or virtctl port-forward to guest port 22 Guest shell over SSH Kubernetes API forwarding into the virt-launcher pod, then to the guest’s port 22 No
NodePort 30080 External HTTP validation only Node public IPv4, then Service, then pod port 80, then masquerade DNAT to guest port 80 Yes, from the operator /32 only

29.5 Review R5: pre-transfer boot test (mandatory gate)

This test proves the exact prepared artifact before paid-cloud transfer. It is a mandatory gate between image preparation and transfer (section 18, step 2). It is documented only and was not performed.

Element Requirement
Where conversion-host-01 (ADR 006)
What The exact prepared qcow2 whose sha256 is in the preparation record (section 29.2). It is opened read-only as the backing file of a disposable overlay, as in Stage 1G section 9, so its bytes cannot change. Its sha256 is re-verified after the test and must still equal the record. Any test-harness instrumentation (such as Stage 1G’s serial autologin) lives only in the overlay, never in the prepared image
Hypervisor QEMU/KVM, as in Stage 1G
Firmware UEFI with OVMF, Secure Boot off, a fresh copy of the variable-store template for this boot, as in Stage 1G section 7
Disk virtio-blk
Network virtio-net on QEMU user-mode networking, isolated (restrict=on, as in Stage 1G), using its built-in DHCP server; host port forwards for HTTP (and SSH, if used)
Guest agent A virtio-serial port named org.qemu.guest_agent.0, the channel name KubeVirt uses

Pass conditions (all required):

  1. The guest reaches the multi-user target.
  2. DHCP netplan works. The NIC matched by driver virtio_net obtains a DHCP lease from the user-mode network and has a default route. The address itself is QEMU’s, not the KubeVirt 10.0.2.2, and is not a pass condition.
  3. systemctl is-system-running reports running: no pending first-boot job and no failed unit.
  4. The guest agent responds on the virtio-serial channel (for example, to a ping command of the agent protocol).
  5. nginx answers through the host port forward with HTTP 200.
  6. The page sha256 equals b9826e18...0046.
  7. Identity spot checks match the baseline: filesystem UUIDs, machine-id, SSH host key fingerprints. None of the removed first-boot files is present.

Not proven by this test:

On failure:

Cleanup:

29.6 Review R6: transfer constraints

The route remains DEFERRED to Stage 1I: directly from conversion-host-01, or through Windows. S3 is not part of this design. Whatever the route, the transfer must satisfy all of the following (PROPOSED DESIGN):

  1. Outbound-only initiation from the lab side. There is no inbound connection into the laptop, VMnet8 or the lab VMs.
  2. SSH transport to the node’s own sshd (for example scp or rsync over SSH). There is no public upload endpoint and no exposed CDI upload proxy.
  3. Build-time node key, never committed.
    • The node holds only the public key.
    • If the transfer starts from a host other than the operator workstation (for example conversion-host-01), where any copy of the private key lives is recorded, and that copy is removed at the end of the session.
  4. Operator /32 restriction. The security group admits only the transfer’s source address. Through Workstation NAT, that is normally the operator’s public address (ENGINEERING INFERENCE). It is never widened.
  5. SHA-256 verification at both ends. The hash is taken on the source side (in the preparation record) and recomputed on the node. They must match before any upload.
  6. The authoritative artifact stays on the controlled source side (read-only on conversion-host-01). The transfer never moves or deletes it.
  7. A partial or corrupt destination file is discarded. A file that is partial or has a mismatched hash is deleted and the transfer repeated. It is never uploaded.
  8. AWS-side handling is explicit.
    • The qcow2 sits in one recorded path on the encrypted root volume, readable only by the administrative user.
    • No private key is placed on the node.
  9. Deletion after import. After a successful import (DataVolume Succeeded), the qcow2 is deleted from the node, unless it is explicitly retained as evidence (for example, until the guest checks pass). A retained copy is deleted in teardown step 8 (section 29.1) and never outlives the session.

29.7 Review R7: encryption and artifact sensitivity

Encryption at rest (PROPOSED DESIGN):

Disk artifacts are sensitive data (ENGINEERING INFERENCE from the guest’s contents; PROJECT-DERIVED FACT that host keys and machine-id are preserved, Stage 1G). Every VMDK, qcow2 and EBS copy of this guest may contain:

Handling requirements:

29.8 Review R8: point-in-time and no-cutover semantics

PROJECT-DERIVED FACT (Stage 1E, Stage 1H H1):

Consequences (ENGINEERING INFERENCE):

Gates

Gate Evidence Result
H1 Repository and lab state main = origin/main = a7aa1f54e7b033f9619a46057063c8a3121796c8 (Stage 1G), clean tree; source HTTP 200, 267 bytes, page sha256 b9826e18...0046 from Windows and ESXi; Vmid 1 off, Vmid 2 and 3 on, 0 snapshots; golden descriptor and flat hashes, sizes, timestamps and ReadOnly attribute unchanged PASS
H2 Official compatibility Sections 6 to 8: KubeVirt v1.9.0, Kubernetes 1.34 to 1.36, CDI v1.66.1, runtimes, architecture; one tuple from KubeVirt CI PASS
H3 Host requirements Section 9, with virt-host-validate qemu as the planned (not executed) check PASS
H4 EKS vs self-managed Sections 10 to 12, with the AWS nested-virtualization list read from AWS docs [E32] and EKS evaluated on its own evidence PASS
H5 Platform decision ADR 007 PASS
H6 Network analysis Section 13 (four bindings), section 14 (.31 does not survive), ADR 008 PASS
H7 Guest netplan design Section 15 (DESIGN only), ADR 010 PASS
H8 Storage and format Sections 16 and 17, ADR 009 PASS
H9 Guest agent Section 19 PASS
H10 End-to-end design and documentation Section 20, three diagrams, this record, index updates, validation, commit PASS

Sources

E1 to E49 retrieved on 2026-09-28. E50 to E56 retrieved on 2026-09-30 for the post-review corrections (section 29). Local evidence (outside Git): C:\VMs\conversion-host-01\stage-1h\H1.txt and h1.ps1.

# Source (URL) Title Exact requirement or fact Implication
E1 https://github.com/kubevirt/kubevirt/releases Releases - kubevirt/kubevirt v1.9.0 published 2026-07-30, promotion of v1.9.0-rc.2; newer only v1.10.0-alpha.0 (pre-release) Target v1.9.0
E2 https://storage.googleapis.com/kubevirt-prow/release/kubevirt/kubevirt/stable.txt KubeVirt stable release pointer Returns v1.9.0 Confirms the stable release
E3 https://kubevirt.io/user-guide/release_notes/ Release notes - KubeVirt user guide “KubeVirt v1.9 is built for Kubernetes v1.36 and additionally supported for the previous two versions.” Kubernetes 1.34 to 1.36
E4 https://github.com/kubevirt/sig-release/blob/main/releases/k8s-support-matrix.md KubeVirt to Kubernetes version support matrix 1.9: 1.36, 1.35, 1.34; not 1.37 1.37 excluded
E5 https://kubevirt.io/user-guide/cluster_admin/installation/ Installation - KubeVirt user guide Latest three Kubernetes releases at KubeVirt release time; apiserver --allow-privileged=true; runtimes containerd, crio; AppArmor caveats; virt-host-validate qemu checks /dev/kvm, /dev/vhost-net, /dev/net/tun; EL-based virt-launcher userland, divergent non-EL kernels not recommended; SELinux needs container-selinux; useEmulation fallback Host checklist (section 9)
E6 https://github.com/kubevirt/kubevirt/blob/v1.9.0/hack/rpm-deps.sh kubevirt v1.9.0 hack/rpm-deps.sh Default CentOS Stream 9; libvirt 0:11.10.0-12.el9, QEMU 17:10.1.0-20.el9 EL9 host kernel is the aligned choice
E7 https://github.com/kubevirt/kubevirt.github.io/blob/main/_posts/2026-09-03-understanding-kubevirt-host-kernel-userspace-dependencies.md Understanding KubeVirt’s Host Kernel and Userspace Dependencies (kubevirt.io blog) Release images built on CentOS Stream 9; upstream CI runs on EL9 host kernels (5.14); non-EL hosts are not covered by upstream CI (issue #16386) CentOS Stream 9 nodes; EKS AMIs misaligned
E8 https://github.com/kubevirt/project-infra/blob/main/github/ci/prow-deploy/files/jobs/kubevirt/kubevirt/kubevirt-presubmits-1.9.yaml KubeVirt release-1.9 presubmit jobs Lanes on k8s-1.33 to 1.36, incl. sig-compute, sig-network, sig-storage on 1.36 Kubernetes 1.36 is CI-proven for v1.9
E9 https://github.com/kubevirt/kubevirtci/tree/main/cluster-provision/k8s/1.36 kubevirtci cluster-provision/k8s/1.36 version 1.36.5; base centos9; CRI-O 1.36; flannel; kubeadm; cgroupDriver: systemd; container-selinux; fetch-latest-cdi.sh The tuple mirrors KubeVirt CI
E10 https://github.com/kubevirt/kubevirt/releases/download/v1.9.0/feature-gates.json v1.9.0 feature-gates.json PasstBinding Beta, Snapshot Beta; no BlockVolume gate listed passt is Beta; block volumes not gated
E11 https://github.com/kubevirt/kubevirt.github.io/blob/main/_posts/2026-07-10-Beta-Features-On-By-Default-In-v1-9.md Beta Features Enabled by Default in KubeVirt v1.9 All Beta feature gates enabled by default from v1.9 Security note (section 23)
E12 https://github.com/kubevirt/kubevirt/releases/tag/v1.9.0 KubeVirt v1.9.0 release notes cgroup v1 deprecated, removal planned next release cgroup v2 node
E13 https://github.com/kubevirt/containerized-data-importer/releases Releases - containerized-data-importer v1.66.1 2026-09-06 (latest); v1.66.0 2026-08-05 “Update kubevirtci, 1.36 lanes” CDI v1.66.1
E14 https://github.com/kubevirt/containerized-data-importer/blob/v1.66.1/doc/supported_operations.md CDI supported operations Formats qcow2, VMDK, VDI, VHD, VHDX, raw (xz/gz) all converted to raw; sources incl. upload qcow2 upload, raw target
E15 https://github.com/kubevirt/containerized-data-importer/blob/v1.66.1/README.md CDI README kubevirt content type: convert qcow2 to raw, resize to all available space; NFSv3 not supported Size PVC = virtual size
E16 https://github.com/kubevirt/containerized-data-importer/blob/v1.66.1/doc/upload.md CDI upload cdi-uploadproxy must be reachable; kubectl port-forward -n cdi service/cdi-uploadproxy 8443:443; virtctl image-upload Upload on the node via port-forward
E17 https://github.com/kubevirt/containerized-data-importer/blob/v1.66.1/doc/scratch-space.md CDI scratch space Upload needs scratch; scratch always Filesystem, RWO Class must serve Filesystem too
E18 https://github.com/kubevirt/containerized-data-importer/blob/v1.66.1/doc/block_cri_ownership_config.md device_ownership_from_security_context CRI configurable Required for block PVCs; CRI-O [crio.runtime] device_ownership_from_security_context = true CRI-O setting
E19 https://github.com/kubevirt/kubevirt/blob/v1.9.0/go.mod kubevirt v1.9.0 go.mod kubevirt.io/containerized-data-importer-api v1.64.0 DataVolume API compatibility evidence
E20 https://kubevirt.io/user-guide/network/interfaces_and_networks/ Interfaces and Networks - KubeVirt user guide Bindings bridge, sriov, masquerade (nftables NAT), passtBinding; masquerade only on the pod network; guest should use DHCP; expose via a Service to survive restarts; bridge on pod network: no live migration; Multus needed for secondary networks; macvlan/ipvlan invalid for bridge; passt +250Mi, feature gate Section 13
E21 https://github.com/kubevirt/kubevirt/blob/v1.9.0/staging/src/kubevirt.io/api/core/v1/schema.go and https://github.com/kubevirt/kubevirt/blob/v1.9.0/pkg/network/link/address.go KubeVirt v1.9.0 API schema and masquerade address code vmNetworkCIDR default 10.0.2.0/24; gateway = 2nd address, guest = 3rd (10.0.2.1, 10.0.2.2) Guest address 10.0.2.2
E22 https://kubevirt.io/user-guide/compute/live_migration/ Live Migration - KubeVirt user guide PVCs must be RWX to live migrate; bridge on pod network not allowed Cold migration needs neither
E23 https://kubevirt.io/user-guide/storage/disks_and_volumes/ Disks and Volumes - KubeVirt user guide bus: virtio; filesystem PVC needs raw disk.img; block PVC consumed as raw device; DataVolume behaviour; containerDisk Block PVC, virtio
E24 https://kubevirt.io/user-guide/user_workloads/guest_agent_information/ Guest Agent information - KubeVirt user guide GA is optional; AgentConnected condition; guestosinfo, userlist, filesystemlist Agent non-blocking
E25 https://kubernetes.io/releases/ and https://dl.k8s.io/release/stable-1.36.txt Releases - Kubernetes; stable-1.36 pointer Maintained 1.37, 1.36, 1.35; EOL 1.36 2027-06-28, 1.34 2026-10-27; stable-1.36 = v1.36.5; stable = v1.37.1 Kubernetes 1.36
E26 https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/ Installing kubeadm Red Hat instructions via pkgs.k8s.io per minor; runtime sockets (CRI-O /var/run/crio/crio.sock); swap handling; SELinux note kubeadm on CentOS Stream 9
E27 https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/ Creating a cluster with kubeadm Single machine: kubectl taint nodes --all node-role.kubernetes.io/control-plane- Single-node scheduling
E28 https://kubernetes.io/docs/concepts/architecture/cgroups/ About cgroup v2 cgroup v1 deprecated since 1.35; kubelet fails on v1 by default; RHEL 9-like distros use v2 cgroup v2
E29 https://github.com/cri-o/packaging and https://github.com/cri-o/cri-o/releases cri-o/packaging README; CRI-O releases Packages from download.opensuse.org/repositories/isv:/cri-o per minor; v1.36.6 on 2026-09-21 CRI-O 1.36
E30 https://www.centos.org/download/ Download - The CentOS Project CentOS Stream 9 EOL 2027-05-31; Stream 10 EOL 2030-05-31 Lab lifetime fits
E31 https://www.centos.org/download/aws-images/ CentOS AWS AMI Cloud Images CentOS Stream 9 x86_64 ap-south-1 ami-0e7930d02f47291cb; cloud user ec2-user Node image exists in the Region
E32 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-ec2-nested-virtualization.html Use nested virtualization to run hypervisors in Amazon EC2 instances Families M7i, M7i-flex, M8i, M8id, M8i-flex, C7i, C7i-flex, C8i, C8id, C8i-flex, R7i, R7iz, R8i, R8id, R8i-flex, X8i, I7i, I7ie; KVM and Hyper-V as L1; no additional cost; enable at launch or on a stopped instance; check with describe-instance-types; bare metal recommended for performance-sensitive work m8i.xlarge with nested virtualization
E33 https://aws.amazon.com/about-aws/whats-new/2026/06/nested-virtualization-intel-us-gov-cloud/ Nested virtualization is now available on additional Intel platforms and AWS GovCloud (US) regions 2026-06-18: 7th-gen families added; support in all commercial Regions ap-south-1 in scope (U1)
E34 https://docs.aws.amazon.com/linux/al2023/ug/virtualization-getting-started.html Get started with virtualization on Amazon Linux 2023 Processor extensions only on bare metal or with nested virtualization on; missing /dev/kvm almost always means it was not enabled /dev/kvm check
E35 https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html Understand the Kubernetes version lifecycle on EKS Standard support 1.36, 1.35, 1.34; 1.36 on EKS 2026-06-02 EKS version fine
E36 https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html Customize managed nodes with launch templates Prohibited: subnet, IAM instance profile, shutdown/hibernate behaviour (CPU options not listed) EKS /dev/kvm path plausible, undocumented
E37 https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-amis.html Create nodes with pre-built optimized images Amazon Linux, Bottlerocket, Ubuntu, Windows AMIs; custom AMIs possible No EL-aligned EKS AMI
E38 https://docs.aws.amazon.com/eks/latest/best-practices/pod-security.html and https://aws.amazon.com/blogs/containers/implementing-pod-security-standards-in-amazon-eks/ Pod Security (EKS best practices); Implementing Pod Security Standards in Amazon EKS (AWS Containers blog) Privileged PSS level; EKS default PSA configuration is privileged for all modes EKS admits privileged DaemonSets
E39 https://docs.aws.amazon.com/eks/latest/userguide/pod-multiple-network-interfaces.html Attach multiple network interfaces to Pods By default the VPC CNI assigns one IP per pod from an ENI EKS pod networking model
E40 https://github.com/kubernetes-sigs/aws-ebs-csi-driver (README, docs/install.md, releases) Amazon Elastic Block Store (EBS) CSI driver Raw block volumes; compatible with all upstream-supported Kubernetes versions; IAM AmazonEBSCSIDriverPolicyV2 scoped to tag ebs.csi.aws.com/cluster: true; IMDSv2 needs hop limit >= 2 unless host network; v1.66.0 on 2026-09-10 Storage design
E41 https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html General Purpose SSD volumes gp3 baseline 3,000 IOPS and 125 MiB/s; 1 GiB to 64 TiB gp3 sizing
E42 https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/ec2/USD/current/ec2-ondemand-without-sec-sel/Asia%20Pacific%20(Mumbai)/Linux/index.json AWS public price file, EC2 on-demand Linux, Mumbai (published 2026-09-25) m8i.xlarge 0.2227, m7i.xlarge 0.2121, m7i.metal-24xl 5.0904 USD/h; all listed nested families sold in the Region Cost model
E43 https://b0.p.awsstatic.com/pricing/2.0/meteredUnitMaps/ec2/USD/current/ebs.json AWS public price file, EBS (published 2026-09-25) gp3 storage in Mumbai 0.0912 USD/GB-month Cost model
E44 https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEKS/current/ap-south-1/index.json AWS Price List, AmazonEKS ap-south-1 (published 2026-09-18) 0.10 USD per cluster-hour; extended support 0.50 USD/h EKS cost
E45 https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonVPC/current/ap-south-1/index.json AWS Price List, AmazonVPC ap-south-1 (published 2026-09-17) Public IPv4 0.005 USD/h (in use or idle) Cost model
E46 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html Elastic network interfaces ENI IPv4 addresses come from the subnet range; source/destination checks (on by default) ensure the instance is the source or destination of its traffic .31 cannot appear in a VPC
E47 https://github.com/flannel-io/flannel/releases Releases - flannel-io/flannel v0.28.9 on 2026-08-07 CNI version
E48 https://github.com/kubernetes/kubernetes/blob/release-1.36/cmd/kubeadm/app/phases/controlplane/manifests.go kubeadm control-plane manifests (release-1.36) {Name: "allow-privileged", Value: "true"} Privileged API server by default
E49 https://github.com/kubevirt/kubevirt/blob/v1.9.0/pkg/virtctl/imageupload/imageupload.go virtctl image-upload (v1.9.0) Flags --volume-mode, --access-mode, --storage-class, --uploadproxy-url, --insecure, --force-bind, --size Upload command design
E50 https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/v1.66.0/docs/parameters.md AWS EBS CSI driver v1.66.0 StorageClass parameters encrypted: true/false, default false; kmsKeyId: if not specified, AWS uses the default KMS key for the Region Explicit encrypted: "true" (section 29.7)
E51 https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AmazonEBSCSIDriverPolicyV2.html AmazonEBSCSIDriverPolicyV2 (AWS managed policy) Create, modify and delete limited to volumes tagged ebs.csi.aws.com/cluster = true (or kubernetes.io/created-for/pvc/name for migrated volumes) Post-destroy orphan check by tag (section 29.1)
E52 https://github.com/flannel-io/flannel/releases/download/v0.28.9/kube-flannel.yml flannel v0.28.9 deployment manifest net-conf.json: "Network": "10.244.0.0/16", backend vxlan Pod CIDR (section 29.3)
E53 https://github.com/kubernetes/kubernetes/blob/release-1.36/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go kubeadm v1beta4 defaults (release-1.36) DefaultServicesSubnet = "10.96.0.0/12", DefaultClusterDNSIP = "10.96.0.10" Service CIDR (section 29.3)
E54 https://github.com/cri-o/cri-o/blob/release-1.36/contrib/cni/11-crio-ipv4-bridge.conflist CRI-O release-1.36 default bridge CNI configuration Bridge network crio with subnet 10.85.0.0/16 Range kept clear (section 29.3)
E55 https://github.com/kubevirt/containerized-data-importer/blob/v1.66.1/doc/datavolumes.md Data Volumes - CDI v1.66.1 documentation “Data Volumes(DV) are an abstraction on top of Persistent Volume Claims(PVC)”; the DV “will monitor and orchestrate the import/upload/clone of the data into the PVC”; the pvc and storage sections both “result in CDI creating a PVC resource” The standalone DataVolume is the migration disk’s lifecycle object (sections 16, 18, 29.1)
E56 https://github.com/kubevirt/containerized-data-importer/blob/v1.66.1/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/types.go CDI v1.66.1 API types DataVolumeTTLSeconds: “the time in seconds after DataVolume completion it can be garbage collected”; “Deprecated: Removed in v1.62” The DataVolume is expected to remain after Succeeded (INFERRED; section 18)