Installation¶
VirtFoundry installs the control plane (API, UI). Platform state is stored in virtfoundry.io CRDs. Virtual machines and networking rely on KubeVirt, Multus, and CDI on the cluster.
Install prerequisites first
See Platform prerequisites for official install links to KubeVirt, Multus, CDI, storage, and optional MetalLB / CSI snapshots.
Recommended order: prerequisites → virtfoundry-operator (CRDs + controller) → virtfoundry (API + UI).
Want UI + first VM in under 30 minutes? Start with the Quickstart. On a laptop (Docker, no switch/VLAN), use Kind.
For minimum vs production layouts (what works for VPC / public / snapshots on a home router), see Deployment topologies.
Prerequisites overview¶
| Component | Required? | Role in VirtFoundry |
|---|---|---|
| Kubernetes 1.28+ | Yes | Runs all workloads |
| Helm 3.x | Yes | Installs the charts |
| virtfoundry-operator | Yes (CRD store) | Installs virtfoundry.io CRDs and reconciles Tenant/Instance status |
| KubeVirt | Yes | Hypervisor — VMs, start/stop, console, VM snapshots |
| Multus CNI | Yes | Secondary NICs — tenant VPCs, isolated L2, public VM network |
| CDI | Yes for ISO/import templates; optional for container-disk-only | Imports ISOs and blank boot disks via DataVolume |
| Ingress or Gateway API + controller | One of them | Exposes UI and API on a hostname |
| StorageClass — prefer Longhorn | Yes for disks | PVCs for VM volumes, ISO storage; local-path only for quick labs |
| CSI snapshotter + snapshot-capable CSI (Longhorn includes this) | Recommended; required for volume snapshots UI | VolumeSnapshot CRDs + VolumeSnapshotClass; not provided by local-path |
| MetalLB (or cloud LB) | Bare metal only | When Services need external IPs |
Not bundled in the Helm chart by default
KubeVirt, Multus, and CDI are cluster-scoped platform operators. They are installed separately so you can pin versions, align with your distro, and upgrade them independently of VirtFoundry releases.
Why each platform component is needed¶
KubeVirt — required¶
VirtFoundry does not embed a hypervisor. The API talks to KubeVirt CRDs (and the operator syncs Instance status back to virtfoundry.io CRs):
VirtualMachine/VirtualMachineInstance— create, start, stop, delete VMsVirtualMachineSnapshot— VM snapshots (point-in-time of the guest; not the same as CSI volume snapshots)- VNC subresource — web console in the UI
Without KubeVirt, deploy and lifecycle operations fail immediately (kubevirt.enabled assumes the KubeVirt API is reachable).
Volume snapshots ≠ VM snapshots
The Volume Snapshots page creates Kubernetes VolumeSnapshot objects (snapshot.storage.k8s.io). That API is not installed by KubeVirt and is not available with only local-path. Without CSI external-snapshotter + a snapshot-capable StorageClass (Longhorn, Ceph RBD, cloud CSI, …), the UI returns the server could not find the requested resource. Use VM Snapshots on lab/local-path clusters, or install a CSI snapshot stack for volume snapshots. Details: Configuration — Snapshots.
Verify:
kubectl get pods -n kubevirt
kubectl get crd virtualmachines.kubevirt.io
Multus CNI — required¶
VirtFoundry models multi-tenant networking: tenants, VPCs, security groups, and an optional shared public network. That requires more than the default pod CNI:
| Feature | How VirtFoundry uses Multus |
|---|---|
| Tenant VPC / private networks | Creates NetworkAttachmentDefinition (NAD) per network on an isolated bridge |
| Public / routable VM IPs | Secondary NIC on a bridge or macvlan NAD + cloud-init addressing |
| Security groups | Kubernetes NetworkPolicy on the pod network; extra NICs use Multus interfaces |
The hypervisor driver attaches Multus networks to VM launcher pods (v1.multus-cni.io/default-network and additional NADs). VPCs, custom networks, and public IP pools do not work without Multus.
Pod-only VMs (no network_ids, public network disabled) still use KubeVirt’s masquerade interface, but the product expects Multus for full IaaS functionality.
Verify:
kubectl get pods -n kube-system -l app=multus
kubectl get crd network-attachment-definitions.k8s.cni.cncf.io
CDI — required for ISO and import workflows¶
CDI provides DataVolume resources. VirtFoundry uses CDI when:
- Registering an ISO template (HTTP import of an
.isointo a PVC) - Creating a blank boot disk for install-from-ISO (e.g. Windows eval)
- Waiting for import completion before a VM can boot from ISO
Container-disk templates (image URL pointing at a registry-hosted disk image) can work without CDI — KubeVirt pulls the image directly as a containerDisk.
| Template / deploy path | CDI needed? |
|---|---|
| Linux cloud image (container disk) | No |
| ISO template or install-from-ISO | Yes |
| Attaching an existing imported volume | Yes (volume created via CDI) |
If you only use container-disk templates, you can skip CDI initially; enable it before using ISO features.
Verify:
kubectl get pods -n cdi
kubectl get crd datavolumes.cdi.kubevirt.io
Installing platform components¶
The chart can optionally trigger install hooks (platform.multus.install, platform.cdi.install, KubeVirt job). By default these are off — most clusters install platform software once, outside VirtFoundry upgrades.
Recommended: use the helper scripts (idempotent) from a chart clone:
export KUBECONFIG=/path/to/kubeconfig
./scripts/setup/kubevirt.sh # KubeVirt operator + CRDs
./scripts/setup/multus.sh # Multus DaemonSet
./scripts/setup/cdi.sh # CDI operator
Or install from upstream docs and verify CRDs before proceeding.
Order: Multus and storage class first → KubeVirt → CDI (CDI depends on KubeVirt CRDs).
Install VirtFoundry from Helm repository¶
After platform prerequisites are healthy:
helm repo add virtfoundry https://virtfoundry.github.io/helm-charts
helm repo update
# 1. CRDs + operator (required)
helm install virtfoundry-operator virtfoundry/virtfoundry-operator \
--namespace virtfoundry-system \
--create-namespace
# 2. API + UI
helm install virtfoundry virtfoundry/virtfoundry \
--namespace virtfoundry-system \
--set secrets.rootPassword='choose-a-strong-password' \
--set secrets.jwtSecret="$(openssl rand -hex 32)"
The chart ships no credential defaults — see Secrets for the
rules and for the secrets.existingSecret path.
Pin a release (same CRD store flags):
helm install virtfoundry-operator virtfoundry/virtfoundry-operator \
--version 0.7.3 \
--namespace virtfoundry-system \
--create-namespace
helm install virtfoundry virtfoundry/virtfoundry --version 0.7.3 \
--namespace virtfoundry-system \
--set secrets.rootPassword='choose-a-strong-password' \
--set secrets.jwtSecret="$(openssl rand -hex 32)"
Images default to ghcr.io/virtfoundry/core:0.7.3, ui:0.7.3, and operator:0.7.3.
Why --set is on the Helm command¶
--set is a Helm flag. It must appear on the same helm install / helm upgrade line as the chart. It does not work as a follow-up kubectl command.
- Must set at install:
secrets.rootPassword,secrets.jwtSecret— orsecrets.existingSecret. The chart has no defaults for them and fails to render without one of the two (Secrets). - Usually omit: public IP CIDR — see below. Storage class —
autopicks Longhorn when it exists. - Prefer
-f: anything more than two keys. Written defaults: Chart values.
Longhorn — recommended storage (not bundled)¶
VirtFoundry never ships disks. platform.storage.defaultClass: auto selects longhorn if that StorageClass is already on the cluster, otherwise the cluster default, otherwise local-path.
Install Longhorn first if you want replicated VM disks and volume snapshots. local-path is fine for a first VM; volume snapshots need CSI (Longhorn provides that). Pin the class with --set platform.storage.defaultClass=longhorn if auto is not enough.
Public IP — optional; homelab can inherit the node LAN¶
Leave public unset (enabled: false, chart default) to install the UI. VMs stay on the pod network.
If you enable public later, CIDR/gateway/pool default to the first Node InternalIP (autoFromCluster: true) so a single-LAN homelab does not invent 10.0.50.0/24. That is the Kubernetes address, not a VLAN. Dedicated VM VLANs (this project's homelab uses 10.0.50.0/24 on enp3s0.50) must set CIDR by hand and autoFromCluster: false. Auto never sets uplink (that would steal the kubelet NIC).
Script: scripts/detect-host-public-net.sh. Full values: Chart values. Underlay choices: Topologies.
Install from git clone¶
git clone https://github.com/virtfoundry/helm-charts.git
cd helm-charts
helm install virtfoundry-operator ./charts/virtfoundry-operator \
--namespace virtfoundry-system --create-namespace
helm install virtfoundry ./charts/virtfoundry \
--namespace virtfoundry-system \
--set secrets.rootPassword='choose-a-strong-password' \
--set secrets.jwtSecret="$(openssl rand -hex 32)"
Validate templates:
make lint
First login¶
Bootstrap credentials come from the chart — there is no built-in default password:
- User:
root - Password: the
secrets.rootPasswordyou passed at install (orROOT_PASSWORDin yoursecrets.existingSecret)
API base path: /api/v1 on the same hostname as the UI.
Verify CRD store¶
After install:
kubectl get crd | grep virtfoundry.io
kubectl get vf-tenant
kubectl get vf-instance -A
kubectl get vmsnapshot -A
kubectl get pods -n virtfoundry-system
You should see virtfoundry-operator and virtfoundry-api Running, Instance CRs for your VMs, and KubeVirt VirtualMachineSnapshot objects when you use VM Snapshots in the UI.
Next steps¶
- Quickstart — under-30-minute UI + first VM path
- Configuration — Helm values and networking (includes
platform.storage.snapshotClass) - Chart values (defaults) — full
values.yaml, why--set, Longhorn and public IP - Helm repository — publishing and consuming chart releases