The first question in a Victor evaluation is usually “what hardware do I need?” It is the wrong first question. Victor installs five different ways, and each one implies different hardware, a different database, a different update model and a different amount of work before you see a VM. Pick the path first; the machine follows.

This guide covers each path, what it actually installs, how to check that a machine is ready, how to verify what you downloaded, and what to do in the first hour after install. Victor’s repositories are private at the time of writing, so repository access may be required for the linked docs.

The five paths at a glance#

Path What you get Runs on Database Status
Victor Host ISO An image-mode AlmaLinux 9 host with KVM (through Incus), PostgreSQL and Victor Bare metal or a VM, x86-64-v2 PostgreSQL 16, local Published as v0.1.0-pre.2 (single host)
Appliance victord, PostgreSQL and metrics in one VM, managing the Incus cluster it runs on An existing Incus server or cluster PostgreSQL 16, inside the VM Built from source (make host-appliance), not in a published release
Helm victord as a Kubernetes Deployment Kubernetes 1.29+ External Postgres, CloudNativePG, or one lab Postgres Chart in the repository and in the air-gap bundle
Air-gapped bundle Signed images, chart, CLIs, Terraform provider mirror, docs A disconnected Kubernetes cluster As for Helm Built with make bundle
Evaluation installer Incus, PostgreSQL and victord on an existing Linux VM Ubuntu 24.04+ or Debian 12+ PostgreSQL, local Published (install.sh and binaries)

Before anything: check host readiness#

Whatever you choose, any machine that will run VMs should pass victor-hwcheck first. It ships in the bundle’s bin/, in the Victor Host image and as a release asset for Linux amd64 and arm64. It changes nothing and needs no network:

./victor-hwcheck --data-path /var/lib/incus --bmc https://bmc-01.example.mil
./victor-hwcheck --json > hwcheck-$(hostname).json   # same results, for automation

Victor keeps no hardware compatibility list. The check reports pass, warn or fail, with the fix, for virtualization extensions and /dev/kvm (fail without them), IOMMU for passthrough, TPM 2.0, UEFI and Secure Boot, memory, free disk under --data-path, NICs and drivers, CPU vulnerability mitigations, whether the machine is itself a VM, and, with --bmc, whether fencing can reach the BMC. Its “Host OS” line tells you whether the machine can take Victor Host or needs a distribution of its own. The exit status is 1 when a host isn’t ready, so it fits in a provisioning pipeline.

Path 1: Victor Host ISO#

Victor Host is the host OS Victor ships: an image-mode (bootc) AlmaLinux 9 image containing unmodified Incus 7.0 LTS, QEMU/KVM (AlmaLinux’s qemu-kvm rebuilt with SPICE for the graphical console), OVMF, swtpm, PostgreSQL 16, Prometheus, victord, victorctl and victor-hwcheck. Current images also carry Ceph Squid packages, which stay idle until a cluster sets Ceph up. It installs from one offline ISO, Proxmox-style.

Hardware minimums: an x86-64-v2 CPU (Intel Nehalem or AMD Bulldozer onwards; a 2012 Sandy Bridge Xeon is fine) with VT-x or AMD-V, at least 2 cores, 8 GB of RAM and one disk of 64 GB or more. Legacy BIOS and UEFI both work; TPM and Secure Boot are used when present, never required. The ISO is x86-64 only.

The installer erases the disk you select and uses it whole. It asks for the disk, hostname, network (NIC, DHCP or static) and time zone and root password, shows a summary and installs only after you type yes. No network is needed. On first boot, victor-host-setup creates a vmbr0 bridge on the primary NIC (Incus profile lan), a NAT network incusbr0, an LVM thin pool for VM disks in the remaining space, initializes PostgreSQL and starts victord. The console then shows Victor: https://<address> and, on the first boot only, the one-time admin password (also in /root/victor-admin-password, readable by root).

Unattended installs take kernel arguments at the boot menu or in a PXE/virtual-media config, for example victor.auto victor.disk=sda victor.hostname=hv-01 victor.ip=10.0.0.11/24 victor.gateway=10.0.0.1 victor.dns=10.0.0.2 victor.timezone=UTC. Current source builds also take victor.ntp= and victor.join=.

What the published ISO is, and isn’t#

The published v0.1.0-pre.2 ISO is Victor Community, built from commit 96569658, and it is single-host: one machine, no multi-node, no Ceph, no HA. Multi-host join and Ceph-from-three-hosts exist in current source builds, described below, but not in that ISO. Every published measurement came from nested QEMU; real hardware hasn’t been tested yet. Read the release notes before you plan anything beyond a single workstation.

Updates and rollback#

The host updates as a whole image. bootc stages the new image beside the running one and keeps the previous one as the rollback; each step is one reboot. /etc and /var (Incus, PostgreSQL, Victor’s keys and data) carry over; /usr is read-only and comes from the image. bootc’s automatic update timer is off, so the host changes only when you say so.

victor-host status                                                  # booted, staged and rollback images
victor-host update /media/usb/host/victor-host-<v>.oci.tar --reboot # from the bundle or release
victor-host update --registry registry.example.mil/victor/victor-host:<v> --reboot
victor-host rollback --reboot                                       # the previous image boots next

If a <file>.sha256 sits beside the archive, victor-host update checks it before staging. Without --reboot, the image is staged and applies at the next reboot. On a multi-host cluster, use the orchestrated rolling upgrade instead (victorctl host upgrade plan|start|status CLUSTER), which puts one host at a time into maintenance and live-migrates its VMs away first.

Joining hosts (current source builds)#

A cluster grows from the first host. Victor issues a one-time join token, valid for an hour by default and a day at most, revocable and audited. It pins Victor’s TLS certificate by its SHA-256 fingerprint, so the new host can verify it is talking to the right Victor.

# on a workstation with victorctl signed in
victorctl host add prod --cluster-address 10.0.0.11 --maintenance   # prints the token once
victorctl host join-token list prod
# on the new host, as root
victorctl host join --check VJ1.eyJ1Ijoi…   # the pre-flight only
victorctl host join VJ1.eyJ1Ijoi…

--cluster-address is only needed while the cluster is still one server: Victor then turns Incus clustering on. A new host can also join from the installer’s “Join an existing Victor cluster” option or with victor.join=<token> on the kernel command line. Pre-flight checks refuse a join for a different image or Incus version, a duplicate name, a CPU that can’t run the cluster baseline, an MTU mismatch, a missing vmbr0, more than 5 s of clock skew, insufficient storage or existing Incus instances. A refused pre-flight leaves the token unused. A host that was installed as a first host and then joins stops its own Victor for good.

Ceph from three hosts#

Ceph keeps three copies on three hosts and needs a monitor majority, so Victor proposes Ceph only once a cluster has three hosts. The proposal places a monitor and manager on three hosts, an OSD on every unused whole disk of at least 8 GiB (never the system disk), and one replicated RBD pool with size 3, min_size 2.

victorctl ceph status prod                                  # before setup: the proposal
victorctl ceph setup prod                                   # show the layout only
victorctl ceph setup prod --skip /dev/disk/by-id/ata-OLD_DISK --yes --wait
victorctl ceph add-osds prod node-4 /dev/disk/by-id/nvme-SAMSUNG_X --wait

By default the new ceph datastore becomes the default for new VMs and storage fencing is wired into HA; --no-default and --no-fencing turn those off. A two-host site needs a small third host as a witness or other shared storage.

Path 2: the appliance#

The appliance is for a site that already has an Incus server or cluster and wants Victor without dedicating a machine to it. It is one qcow2 Incus VM made from the same Victor Host image, running victord, PostgreSQL 16 and the metrics store, and it registers the cluster it runs on automatically. Its own Incus stays off. It is built from source with make host-appliance and is not in a published release. Import is a handful of Incus commands (incus image import, incus init with user.victor.* configuration keys, an agent device, incus start), with the keys carrying the cluster URL, a trust token, the cluster name and a first admin password; updates are staged from Administration › Appliance, with victor-host rollback available.

Path 3: Helm on Kubernetes#

Use Helm when Victor itself should run as a highly available service on Kubernetes, typically managing separate Incus clusters, KubeVirt clusters, or both. You need Kubernetes 1.29 or newer, Helm 4, and the chart (deploy/helm/victor in the repository or charts/victor-<v>.tgz in the bundle).

The database is the main decision:

Option Chart setting Use for
External HA Postgres (CloudNativePG, Crunchy, a managed service) database.existingSecret or database.dsn Production with an existing Postgres practice
CloudNativePG rendered by the chart: three instances, synchronous replication, WAL archiving to S3 database.ha.enabled=true (install the CloudNativePG operator first) Production without one
One bundled Postgres postgres.enabled=true Labs only: no HA, no backups
kubectl create namespace victor
kubectl -n victor create secret generic victor-db \
  --from-literal dsn='postgres://victor:…@pg-rw.db.svc:5432/victor?sslmode=verify-full'
kubectl -n victor create secret tls victor-tls --cert victor.crt --key victor.key
helm install victor victor-<v>.tgz -n victor \
  --set database.existingSecret=victor-db --set tls.existingSecret=victor-tls \
  --set 'engines={incus}' --set incus.url=https://incus.example.internal:8443 \
  --set incus.existingSecret=victor-incus-identity

engines takes incus, kubevirt or both. Upgrades are helm upgrade with the same values. Uninstalling keeps the generated key Secret (<release>-keys), because without the master key, secrets stored in the database can’t be decrypted.

Path 4: the air-gapped bundle#

For a disconnected site, the bundle (victor-<v>.tar; Enterprise ships its own, victor-enterprise-<v>.tar) carries the images for amd64 and arm64, the chart, victorctl, victor-load and victor-hwcheck, the Terraform provider as a filesystem mirror, SBOMs and docs. Its signed manifest lists every file’s SHA-256 and an expiry date, one year by default. Bundles are built with make bundle; they are not part of the GitHub prereleases.

Bring victor-load across separately (or check its hash against a value received out of band), because it carries the release key everything else is checked against:

victor-load key                                    # fingerprint of the built-in key
victor-load verify victor-<v>.tar                  # signature, expiry, every byte
victor-load push --registry registry.enclave.internal:5000 victor-<v>.tar
helm install victor victor-<v>/charts/victor-<v>.tgz -n victor --create-namespace \
  -f victor-<v>/values-airgap.yaml -f my-values.yaml

verify exits 2 and extracts nothing if the signature fails, the bundle has expired, or any file is missing, extra or changed. An expired bundle must be re-signed by the vendor, not waved through. push verifies again, checks every image’s cosign signature and digest, pushes only to the registry you name, and writes values-airgap.yaml pinning images by digest. Sites that use Zarf can deploy the matching signed package instead: zarf package deploy zarf-package-victor-amd64-<v>.tar.zst on a cluster initialized with zarf init, then helm upgrade with your values. victord logs every endpoint outside the cluster it will contact at startup; review that list before you authorize the deployment.

Path 5: the evaluation installer#

install.sh turns one existing Linux VM into a working Victor: Incus 7.0 LTS from Zabbly’s packages, PostgreSQL, Prometheus and victord. It is for evaluation, demos and development only; production uses the host image.

sudo sh install.sh --binary ./victord-linux-amd64                    # add --mode demo or --sample-vm
sudo sh install.sh --binary ./victord-linux-amd64 --offline ./victor-installer-cache-debian13-amd64

It needs Ubuntu 24.04+ or Debian 12+, amd64 or arm64, and 4 GB of RAM or more. Real VMs need /dev/kvm, so in a cloud VM turn on nested virtualization; without it, Incus system containers stand in for VMs so every screen still works. --with-kubevirt adds k3s and KubeVirt on the same VM so both runtimes show side by side (not available offline yet). Re-running is safe and is the upgrade path.

Verify what you install#

Three things to check, every time:

  1. Checksums and signatures. For a prerelease, reassemble the split ISO and check it, then check the signed SHA256SUMS:

    cat victor-host-v0.1.0-pre.2-x86_64.iso.part* > victor-host-v0.1.0-pre.2-x86_64.iso
    sha256sum -c victor-host-v0.1.0-pre.2-x86_64.iso.sha256
    cosign verify-blob --key victor-release.pub --signature SHA256SUMS.sig --insecure-ignore-tlog=true SHA256SUMS
    sha256sum -c --ignore-missing SHA256SUMS

    Prerelease assets are signed with development keys; treat them as integrity checks, not as a release signature.

  2. Source revision. A published prerelease can be older than the code you are reading about. Compare the commit in the release notes with the server’s: victorctl version prints the client and server version, commit and FIPS mode.

  3. Edition. The Community ISO and bundle contain nothing from Victor Enterprise; Enterprise is a separate server build and bundle activated with a signed license file, not a switch in the Community install. victorctl api SystemService/About reports the server’s edition and the features it offers; on Community there is no License section in the UI.

Decision table by goal#

Goal Path
See the product on one spare machine this afternoon Victor Host ISO (published, single host)
Click through every screen on a cloud VM, no spare hardware Evaluation installer, --mode demo
See KVM and KubeVirt side by side in one disposable VM Evaluation installer with --with-kubevirt
Build a three-host cluster with Ceph and HA Victor Host from a current source build
Add Victor to an existing Incus cluster Appliance (source build) or Helm pointed at it
Run Victor highly available on an existing Kubernetes platform Helm with external Postgres or database.ha
Install in an enclave with no internet Air-gapped bundle with victor-load or Zarf

Pre-install checklist#

  • Path chosen and its status understood (published, source build, or lab only).
  • victor-hwcheck run on every host; failures fixed, warnings understood.
  • Disk to be erased identified (ISO path) and nothing on it you need.
  • Management addresses, gateway, DNS and an NTP server you can reach (an offline box has no time source until you set one).
  • For clusters: a dedicated cluster network, matching MTU on every host, and three hosts with unused disks if Ceph is the plan.
  • For Helm: Kubernetes 1.29+, Helm 4, a database decision, a TLS certificate, and somewhere safe to keep the master key.
  • For air gap: victor-load carried separately, its key fingerprint compared out of band, the bundle’s expiry date checked.
  • Artifacts verified: checksums, signature, commit and edition.

First-day steps after install#

  1. Sign in as admin with the one-time password and set your own. Then create named accounts or connect OIDC or LDAP, and stop using admin day to day.

  2. Point chrony at your NTP server (/etc/chrony.conf) and rerun victor-hwcheck (on Victor Host, victor-host hwcheck) until time sync passes.

  3. Set up the CLI from your workstation with the server’s certificate:

    scp victor:/etc/victor/tls.crt victor-ca.pem
    victorctl login https://victor.example --ca-file victor-ca.pem -u alice
  4. Upload a cloud image as a template and create a test VM; check both the serial and graphical consoles.

  5. Add a backup target and back the test VM up (victorctl backup target add lab --kind dir --path /srv/victor-backups), then restore it.

  6. Record what you installed: victor-host status, victorctl version and the checksums.

Where Victor fits#

All five paths install the same control plane for KVM and KubeVirt virtual machines. For a first look, use the published ISO on one machine or the evaluation installer on a VM. For anything you plan to keep, build from a known commit, verify it, and choose Victor Host or Helm by whether your platform is hosts or Kubernetes.

Compare deployment paths