Zum Hauptinhalt springen
h2kvm PDF deck
HyperCluster — client deck

HyperCluster — Kubernetes cluster lifecycle

HyperCluster turns a set of bare-metal, edge, or private-cloud Linux hosts into a running Kubernetes cluster over ordinary SSH. One config file describes the cluster; one command deploys it. There is no cloud provider API, no bootstrap agent on the nodes, and no daemon to install — just your existing SSH keys, an inventory generated for you, and a kubeconfig on your workstation when it finishes.

It drives two proven provisioning paths — upstream kubeadm via Kubespray for HA production clusters, and k3s with a complete Helm virtualization stack for labs and edge sites — plus RKE2 and MicroK8s adapters, all behind the same lifecycle verbs.

→ Book a demo · Customer manual · Quick start ↓ · CLI reference ↓


Why a lifecycle wrapper​

Kubespray is powerful and correct, but raw: you clone it, hand-write hosts.yaml and four group_vars files, resolve a Python and Ansible version matrix, run a playbook, and then go fetch admin.conf yourself. Every step is a place to get it wrong, and none of it is repeatable across clusters without a private wrapper that every platform team ends up writing twice.

The raw wayThe HyperCluster way
Hand-write hosts.yaml and group_vars per clusterOne cluster.conf generates the full Kubespray inventory and group_vars
Discover mid-deploy that a key is wrong or python3 is 3.9preflight checks local tooling, SSH reachability, sudo, and /dev/kvm up front
scp admin.conf off a control-plane node by handfetch-kubeconfig / merge-kubeconfig land it where kubectl expects it
A lab cluster still needs a day of Helm to run VMsThe k3s path installs Cilium, MetalLB, cert-manager, KubeVirt, and CDI in order
Two operators deploy the same cluster at oncePer-cluster locking on every mutating command, with unlock for stale locks
"Did it actually work?" answered by reading pod logstest, health, and status return structured pass/fail with --json

At a glance​

AreaDetail
EngineKubespray v2.31.0 (Ansible) · k3s · RKE2 · MicroK8s
Kubernetesv1.35.4 default pin (KUBE_VERSION, any Kubespray-supported tag)
ConfigA single cluster.conf → Kubespray inventory + group_vars + kubeconfig
TransportPure SSH, agentless — no cloud API, no node daemon
RuntimePodman default (containerd, docker supported)
CNICilium default; Calico, Flannel, kube-ovn on the Kubespray path
VirtualizationKubeVirt v1.8.4 + CDI v1.65.1, with CNAO, SSP, KubeVirt CSI, snapshot-controller
Discovery5 sources — libvirt, Proxmox, VMware, SSH CIDR scan, CSV
Day-2scale, upgrade, remove-node, destroy, health, test
CLI22 commands · --json on every read path
DesktopNative Cluster Studio (Tauri) driving the same CLI · macOS 13 Ventura+ on Apple Silicon (M1+) · 30-day free trial
Version0.1.0 (initial release, 2026-06) — Desktop app and CLI
LicenceProprietary, all rights reserved; per-seat and team plans
Handoffforge-handoff --json registers the cluster with Zynera

How it works​

cluster.conf
│
├─ preflight ── local tools · SSH reachability · sudo · /dev/kvm
│
├─ init ─────── clone Kubespray @ KUBESPRAY_VERSION
│ generate inventory/hosts.yaml
│ generate group_vars/{all,k8s_cluster}
│
├─ deploy ───── Ansible over SSH → cluster.yml
│ (or k3s / RKE2 / MicroK8s installer over SSH)
│
├─ platform ─── Helm stack: Cilium → cert-manager → MetalLB →
│ snapshot-controller → storage → metrics-server →
│ KubeVirt → CDI → CSI → CNAO → SSP
│
└─ kubeconfig ─ admin.conf → $KUBECONFIG_OUTPUT on your workstation

Nothing in that chain calls a cloud API. The deployer machine — a laptop or a bastion — needs SSH access to the nodes and nothing else, which is what makes the same workflow valid on bare metal, at an edge site, or on a network with no route to the internet.


cluster.conf — the single source of truth​

Every command reads one file. Copy the example, edit the node lists, and you have a complete cluster definition that is diffable, reviewable, and checkable into git.

# ── Identity ────────────────────────────────────────────────
CLUSTER_NAME="mycluster"

# ── Engine ──────────────────────────────────────────────────
K8S_DISTRO="kubespray" # kubespray | k3s | rke2 | microk8s
KUBESPRAY_VERSION="v2.31.0"
KUBE_VERSION="v1.35.4"
CONTAINER_MANAGER="podman" # podman | containerd | docker
CNI_PLUGIN="cilium" # cilium | calico | flannel | kube-ovn

# ── Networking ──────────────────────────────────────────────
KUBE_PODS_SUBNET="10.233.64.0/18"
KUBE_SERVICE_ADDRESSES="10.233.0.0/18"

# ── Node access (SSH only) ──────────────────────────────────
SSH_USER="root"
SSH_KEY="$HOME/.ssh/id_rsa"
SSH_PORT=22

# ── Nodes ───────────────────────────────────────────────────
CONTROL_PLANE_IPS="192.168.1.100"
WORKER_IPS="192.168.1.101 192.168.1.102"

# ── Output ──────────────────────────────────────────────────
KUBECONFIG_OUTPUT="$HOME/.kube/${CLUSTER_NAME}.yaml"

Two ways to avoid writing it from scratch:

hypercluster wizard # interactive generator, no config needed
hypercluster init --profile k3s-remote # start from config/profiles/k3s-remote.conf.example
hypercluster init --profile kubespray-ha # 3-node HA control plane

The Desktop studio ships six starting templates — single-node lab, edge cluster, GPU cluster, KubeVirt lab, production HA, and an air-gapped shape — each of which writes a cluster.conf you can then edit by hand.


Distribution engines​

K8S_DISTRO selects the provisioning engine. preflight, deploy, fetch-kubeconfig, and destroy keep the same shape across all of them.

EngineHow it provisions
kubespray (alias kubeadm)Default. Upstream HA Kubernetes via pinned Kubespray Ansible playbooks over SSH
k3sSingle-binary k3s installed over SSH, followed by the full Helm platform stack
rke2Rancher's hardened distribution — rke2-server on control planes, agents joined by node-token
microk8sSnap install plus microk8s add-node join for Ubuntu fleets
talosImmutable node OS — machine configs are authored with talosctl; HyperCluster fetches the kubeconfig via talosctl kubeconfig using TALOS_ENDPOINT

The Kubespray path​

For production clusters, HyperCluster is an opinionated front end to upstream Kubespray at a pinned tag.

init clones Kubespray at KUBESPRAY_VERSION, installs its Python requirements into a managed environment, and generates the inventory from your node lists:

  • inventory/CLUSTER_NAME/hosts.yaml — control-plane and worker groups derived from CONTROL_PLANE_IPS / WORKER_IPS
  • group_vars/k8s_cluster/k8s-cluster.yml — kube_version, container_manager, kube_network_plugin, pod/service CIDRs, kubelet_cgroup_driver: systemd (required for rootful Podman), and kubeconfig_localhost: true
  • group_vars/k8s_cluster/addons.yml — metrics_server_enabled
  • group_vars/all/all.yml — ansible_user, ansible_ssh_private_key_file, ansible_become

The generator is version-aware: ingress-nginx and the Kubernetes dashboard were removed upstream in Kubespray v2.31.0, so those variables are no longer emitted.

Lifecycle maps onto the canonical playbooks:

CommandPlaybook
deploycluster.yml (typically 15–30 minutes)
scalescale.yml
upgradeupgrade_cluster.yml
remove-noderemove_node.yml with reset_nodes=true
destroyreset.yml with reset_confirmation=yes

The k3s platform stack​

Set K8S_DISTRO="k3s" and a single deploy produces a complete virtualization platform — not a bare cluster you then spend a day layering Helm charts onto.

Host preparation, automatically​

Before k3s is installed, every node is prepared over SSH: br_netfilter, overlay, and the correct kvm_intel / kvm_amd modules are loaded, swap is disabled and commented out of /etc/fstab, Kubernetes sysctls are written to /etc/sysctl.d/99-kubernetes.conf, firewalld is stopped and disabled, and qemu-kvm is installed via dnf or apt.

k3s itself starts deliberately stripped — --flannel-backend=none --disable-network-policy --disable traefik --disable servicelb --disable-kube-proxy — so the platform stack owns networking end to end.

Ordered Helm install​

The stack installs in dependency order, each step health-gated before the next begins:

  1. Cilium — eBPF CNI, kube-proxy replacement, with a pod-API verification gate
  2. cert-manager — in-cluster TLS, ready before any webhook that needs it
  3. MetalLB — L2 address pool from METALLB_IP_POOL so LoadBalancer Services get real IPs
  4. snapshot-controller — plus a default VolumeSnapshotClass
  5. Storage — a default local-path StorageClass when the cluster has none
  6. metrics-server — host-network tuned, so kubectl top works immediately
  7. KubeVirt operator → CDI → disks-images-provider → KubeVirt CSI
  8. CNAO — Multus and multi-network attachments for VMs
  9. SSP — VM templates, with webhook TLS and CA-bundle patching handled automatically
  10. Optional — Longhorn, Descheduler, Node Feature Discovery, NVIDIA GPU Operator
  11. Restart + verify — platform workloads are restarted post-CNI and the snapshot API is verified
PLATFORM_STACK_ENABLED="true"
CERT_MANAGER_ENABLED="true"
METALLB_ENABLED="true"
METALLB_IP_POOL="192.168.1.240-192.168.1.250"
CDI_ENABLED="true"
KUBEVIRT_CSI_ENABLED="true"
SNAPSHOT_CONTROLLER_ENABLED="true"
CNAO_ENABLED="true"
SSP_ENABLED="true"
LONGHORN_ENABLED="false"
GPU_OPERATOR_ENABLED="false"
NFD_ENABLED="false"

Cilium tuning​

Every Cilium capability is a config flag, so the same stack serves a quiet lab and a policy-heavy production network:

VariablePurpose
CILIUM_KUBE_PROXY_REPLACEMENTCilium replaces kube-proxy entirely
CILIUM_HUBBLE_ENABLEDFlow observability
CILIUM_BGP_ENABLEDBGP control plane for route advertisement
CILIUM_WIREGUARD_ENABLEDTransparent node-to-node encryption
CILIUM_GATEWAY_API_ENABLEDGateway API support
CILIUM_HOST_FIREWALL_ENABLEDHost-level network policy
CILIUM_K8S_SERVICE_HOSTAPI host — set to the first control-plane IP on multi-node clusters

Choose Cilium when you intend to run PacketWolf network intelligence or policy-heavy KubeVirt workloads on the cluster.


Virtualization — KubeVirt on the cluster you just built​

KUBEVIRT_ENABLED="true" installs the KubeVirt operator and Containerized Data Importer via Helm, with a release-manifest fallback if the chart is unavailable. It runs at the end of deploy, or standalone:

hypercluster install-kubevirt

Tested combination: KubeVirt v1.8.4 and CDI v1.65.1 on Kubernetes v1.35.x.

Inspect what is running, and the template catalog SSP installed:

hypercluster vms --json
hypercluster vm create --template fedora --name test-vm --json # validate and preview

Preflight guards the prerequisite: if KUBEVIRT_ENABLED is set, it checks for /dev/kvm on the control plane and warns that VMs will need software emulation if the device is missing.


Storage provisioning​

storage inventory, plan, apply, and teardown wrap local-path, lvm-raw, and rook-ceph behind one workflow that refuses to touch a disk holding data:

# Read-only — enumerate block devices per node and report what's safe to use.
# Unmounted Ceph OSDs, LVM PVs, and mdraid members are flagged ineligible.
hypercluster storage inventory

# Preview STORAGE_BACKEND and STORAGE_DEVICES against the inventory
# before anything is touched.
hypercluster storage plan

# Provision the planned backend. Refuses any disk that isn't eligible
# unless it's named exactly via --confirm-destroy.
hypercluster storage apply --confirm-destroy

# Unmount, remove fstab entries, and (with --purge-disks) wipe
# signatures — including purging Rook if the backend was rook-ceph.
hypercluster storage teardown --purge-disks

# Inventory disks by IP and SSH creds directly, no cluster.conf
# required — what the Desktop Studio wizard uses before a cluster exists.
hypercluster storage-probe

Three backends, set from cluster.conf: local-path for a fast default StorageClass, lvm-raw for a dedicated device, or rook-ceph for replicated block storage.


Discovery and preflight​

Find candidate nodes​

discover builds a node list from infrastructure you already run — no cluster.conf required:

hypercluster discover --source libvirt --probe --validate --json
hypercluster discover --source proxmox --host pve.internal --json
hypercluster discover --source vmware --host vcenter.internal --json
hypercluster discover --source ssh-scan --cidr 192.168.1.0/24 --json
hypercluster discover --source csv --csv ./nodes.csv --json
  • --probe SSHes each host and reports vCPU, memory, storage, GPU presence, NIC count, and NUMA layout.
  • --validate checks /dev/kvm, VT-x / AMD-V, KVM modules, IOMMU, nested virtualization, and hugepages — so you know a host can actually run KubeVirt before you build a cluster on it.

probe-nodes does a reachability-only pass when you just need to know which addresses answer.

Fail before you deploy, not during​

hypercluster preflight --json

Preflight verifies, in order:

  • Required commands — ssh, scp, git, kubectl, curl, plus ansible / ansible-playbook / python3 on the Kubespray path or helm on the k3s path
  • Python 3.11+ resolution, with an actionable install hint when the interpreter is too old
  • SSH key presence, and permissions — a key at the wrong mode is corrected to 600
  • SSH connectivity to every control-plane and worker IP
  • Passwordless sudo on the control plane (k3s path), with the exact sudoers.d line to fix it
  • MetalLB IP pool — refuses to proceed if METALLB_ENABLED=true and METALLB_IP_POOL is empty
  • /dev/kvm on the control plane when KubeVirt is enabled

The --json form emits {"ok": bool, "checks": [...]} on stdout with logs on stderr, so CI can gate on it directly.


Lifecycle operations​

Deploy​

hypercluster preflight
hypercluster deploy
hypercluster fetch-kubeconfig
hypercluster test

deploy is the full pipeline — preflight, inventory generation, and cluster bootstrap — with staged progress output. Review it first without touching a node:

hypercluster -n deploy # dry run: print the plan, change nothing

Scale out​

Add the new addresses to WORKER_IPS, then let the engine's own join path run:

hypercluster scale

Upgrade​

Bump KUBE_VERSION in cluster.conf and run one command. On the Kubespray path this drives upgrade_cluster.yml, the supported rolling upgrade:

# cluster.conf: KUBE_VERSION="v1.35.4" → "v1.36.0"
hypercluster upgrade
hypercluster health --with-test --json

If the target Kubernetes version needs a newer Kubespray, move KUBESPRAY_VERSION at the same time.

Retire a node​

hypercluster remove-node worker2 # drains, removes, resets the node
hypercluster -f remove-node worker2 # force: allow ungraceful removal

Tear down​

hypercluster destroy

Destroy refuses to run until you retype the cluster name. On Kubespray it runs reset.yml; on k3s it removes the Helm platform releases and then uninstalls k3s from every node, returning lab hosts to a clean state.

Locking​

Every mutating command takes a per-cluster lock so two operators cannot race a deploy. Read-only commands — status, health, vms, workloads, preflight --json — skip it entirely. If a run is killed mid-flight:

hypercluster unlock

Health, smoke tests, and the JSON interface​

There is no REST server. The machine interface is --json on stdout with logs on stderr, which is exactly what the Desktop app and CI pipelines consume.

hypercluster status --json # node readiness and roles
hypercluster health --json # nodes + pods + services + VMs, one verdict
hypercluster health --with-test # health plus the k3s platform smoke pass
hypercluster workloads --json # pods and services
hypercluster vms --json # KubeVirt VMs and the template catalog
hypercluster test --json # platform smoke tests
hypercluster test --full # adds extended snapshot-API checks

health rolls everything into a single state — healthy, degraded, or unreachable — with the specific issues that produced it (nodes not Ready, pods failed or pending, platform tests failing, API unreachable).

test runs the platform smoke suite against a k3s cluster and returns per-check pass/fail:

  • All nodes Ready, and node count matches cluster.conf
  • Cilium healthy (via cilium status, falling back to a pod check)
  • cert-manager pods Running
  • MetalLB controller Running and IPAddressPool present
  • KubeVirt Available, CDI Available
  • VirtualMachineSnapshot and VolumeSnapshot APIs present
  • metrics-server answering kubectl top nodes
  • Longhorn pods Running (when enabled)
  • A live cirros VM smoke test
  • Remote kubectl over SSH from the control plane

Lab mode​

Single-node k3s plus the full KubeVirt stack does not fit comfortably on one host by default. Lab mode makes it fit.

hypercluster lab storage # mount LAB_SECONDARY_DISK, patch local-path, create StorageClass
hypercluster lab storage --status # inspect the mount and StorageClass
hypercluster lab tune # scale HA replicas down for a one-node host
hypercluster lab tune --restore # undo the tuning
hypercluster lab setup # storage + tune together
LAB_SECONDARY_DISK="/dev/sdb"
LAB_KUBEVIRT_STORAGE_CLASS="kubevirt-sdb"
LAB_KUBEVIRT_VOLUME_MOUNT="/data/kubevirt-volumes"
LAB_MEMORY_TUNE_ENABLED="true"
LAB_AUTO_SETUP="true" # run lab setup at the end of deploy

lab storage keeps bulky VM PVCs off the root and k3s volumes by mounting a dedicated disk and backing a StorageClass with it. LAB_STORAGE_FORMAT="1" will mkfs an empty secondary disk — destructive, and off by default.


Reaching nodes that are hard to reach​

Real fleets are not uniform, and the API server is often not directly routable.

VariableWhat it solves
NODE_SSH_OVERRIDES_JSONPer-node user, key, or port for mixed-vintage hardware that shares no login convention
SSH_PROXY_JUMPReach NAT'd libvirt guests through their hypervisor as a jump host
K3S_API_LOCAL_PORTRewrite the kubeconfig server to 127.0.0.1:PORT behind an SSH tunnel, so the API server never needs public exposure (the customer manual's example local tunnel port is 16443, against the k3s API on 6443)
SSH_EXTRA_ARGSAny additional ssh options, e.g. relaxed host-key checking on ephemeral lab VMs

Handoff to Zynera​

HyperCluster bootstraps the Kubernetes control plane that Zynera runs on. Once nodes are Ready, forge-handoff emits the import payload Zynera needs — cluster identity, API endpoint, kubeconfig path, node lists, and which platform components were actually installed:

hypercluster forge-handoff --json
{
"product": "hypercluster",
"clusterName": "mycluster",
"k8sDistro": "k3s",
"kubeVersion": "v1.35.4",
"apiEndpoint": "https://192.168.1.100:6443",
"kubeconfigPath": "/home/op/.kube/mycluster.yaml",
"cniPlugin": "cilium",
"platformStack": true,
"stackComponents": ["platform_stack", "cilium", "cert_manager", "metallb", "kubevirt", "metrics_server"],
"forge": {
"provider": "hypercluster",
"importPath": "/api/k8s/clusters/import/hypercluster",
"recommendedProfiles": ["velero", "observability", "vectorStore"]
}
}

The same kubeconfig is all any sibling product needs — point PacketWolf, Zynera, and Zeus OS at it and layer on Day-1+ capabilities without re-provisioning anything.


Desktop Cluster Studio​

A native macOS studio drives the identical CLI — every action in the UI is a hypercluster subprocess, so nothing is available in one interface and missing in the other.

  • Create Cluster Hub — Quick Deploy in three steps, or the full Cluster Studio flow (Intent → Nodes → Network → Platform → Validate), which writes a cluster.conf from a typed cluster spec.
  • Cluster cockpit — fifteen sections per cluster: Overview, Nodes, Workloads, Networking, Storage, Virtual Machines, Templates, GPU, Platform, Instruments, Observability, Security, Backups, Upgrades, and Cluster Settings — backed by the same health / status / workloads / vms / test --json output.
  • Live deploy console — streams stdout and stderr with parsed deploy stages and a cancel that sends SIGTERM to the running subprocess.
  • Command palette and editor — ⌘K runs any command; a Monaco editor edits cluster.conf and offers to run preflight on save.
  • AI Copilot — ⌘J for fleet insights, with optional LLM chat that answers in HyperCluster commands rather than raw kubectl.

Security model​

PropertyDetail
Node accessSSH only — no persistent agent is installed on the nodes
CredentialsSSH keys stay on your Mac; no key material is sent off-device
kubeconfigFetched directly to the local kubeconfig path over SSH
TelemetryNo analytics and no update checks

System requirements​

Deployer machine (laptop or bastion)​

ToolMinimumNotes
Bash4.0+macOS ships 3.x — brew install bash
Python3.11+Required by Kubespray v2.31 / Ansible 11
Ansible2.14+ (11.x for Kubespray v2.31+)Kubespray path only
Helm3.xk3s platform-stack path only
kubectlMatching the cluster version
GitAnyUsed to clone Kubespray at the pinned tag

HyperCluster Desktop​

RequirementDetail
macOS13 Ventura or later, Apple Silicon (M1+)
DiskAbout 50 MB installed
AccessPasswordless SSH from the Mac to each node
NetworkInternet is needed during deploy, because Kubernetes images are pulled to the nodes
RuntimeThe packaged app bundles what it needs — no separate Python, Ansible, or Homebrew on the Mac

The bundled runtime applies to the packaged Desktop installer; running the CLI from a source checkout still needs the deployer tools in the table above.

Cluster nodes​

RequirementDetail
OSLinux, SSH-reachable from the deployer
AccessSSH key auth; passwordless sudo (required on the k3s path)
/dev/kvmOn the control plane when KubeVirt is enabled
Secondary diskOptional — LAB_SECONDARY_DISK for VM PVCs on single-node labs
NetworkingPod and service CIDRs that do not overlap the node network

Installation​

Quick start​

git clone https://github.com/zyvorai/hypercluster.git && cd hypercluster
cp config/cluster.conf.example cluster.conf
# edit cluster.conf — nodes, SSH user, K8S_DISTRO

./hypercluster preflight
./hypercluster deploy
./hypercluster fetch-kubeconfig
./hypercluster test

macOS one-time setup​

The macOS system Python and Bash are both too old. Run the installer once — it installs Homebrew Python 3.12, bash 5, Ansible, kubectl, helm, and the Kubespray Python modules:

./scripts/setup-macos.sh

The Desktop app launches with a minimal PATH; it prepends Homebrew Python and Ansible and stores Kubespray data under ~/Library/Application Support/Hypercluster/, including a dedicated kubespray-venv.

Desktop app​

Build the signed installer on an Apple Silicon Mac and open it:

make desktop-pkg # → desktop/distribution/Hypercluster-0.1.0-arm64.pkg
make desktop-pkg-notarized # Developer ID signed + stapled

First launch — Gatekeeper: if macOS reports the app cannot be opened or that Apple cannot check it for malicious software, go to System Settings → Privacy & Security, scroll to the Security section, choose Open Anyway next to the Hypercluster message, then Open. This is needed once.

Packaged installer​

  1. Download Hypercluster-0.1.0.pkg from the release page.
  2. Open the .pkg, then choose Continue, Agree, Install and enter your Mac password when prompted.
  3. The app lands at /Applications/Hypercluster.app.

Trial and licence​

HyperCluster Desktop runs a 30-day per-Mac trial on first launch — all features unlocked, no login. Trial state is bound to the machine ID and stored in the Keychain and ~/.hypercluster/trial.json.

After the trial, email [email protected] with your team size and use case and we will send a licence key JSON file:

make install-license KEY_FILE=./hypercluster-license.json

Or drag the file into the Hypercluster Desktop window when prompted, then restart the app.


CLI reference​

Global options apply to every command: -c FILE config path (default ./cluster.conf), -f force, -n dry run, -v verbose Ansible output, -V version, -h help.

Setup​

CommandDescription
hypercluster wizardInteractive cluster.conf generator — no existing config needed
hypercluster initClone Kubespray and generate inventory plus group_vars
hypercluster init --wizardGenerate the config interactively first, then init
hypercluster init --profile NAMEStart from config/profiles/NAME.conf.example
hypercluster preflightVerify local tools and SSH connectivity to every node (--json)

Lifecycle​

CommandDescription
hypercluster deployFull deployment — preflight, init, cluster bootstrap
hypercluster -n deployDry run — print the plan, change nothing
hypercluster scaleAdd the worker nodes listed in cluster.conf
hypercluster upgradeUpgrade Kubernetes to KUBE_VERSION
hypercluster remove-node NAMEDrain and cleanly remove a single node
hypercluster destroyTear down the cluster (confirmation required)
hypercluster unlockClear a stale per-cluster lock after a crash

Access​

CommandDescription
hypercluster fetch-kubeconfigCopy admin.conf from the first control-plane node
hypercluster merge-kubeconfigMerge the cluster kubeconfig into ~/.kube/config

Discovery and inspection​

CommandDescription
hypercluster discover --source SRCEnumerate nodes from libvirt, Proxmox, VMware, ssh-scan, or csv
hypercluster discover --probeSSH into each host and report hardware
hypercluster discover --validateCheck virtualization readiness per host
hypercluster probe-nodesReachability check without a cluster.conf
hypercluster statusNode readiness and roles (--json)
hypercluster healthNodes, pods, services, VMs — one verdict (--json)
hypercluster health --with-testHealth plus the k3s platform smoke pass
hypercluster workloadsList pods and services (--json)
hypercluster testPlatform smoke tests (--full, --json)

Virtualization and lab​

CommandDescription
hypercluster install-kubevirtInstall the KubeVirt operator and CDI
hypercluster vmsList KubeVirt VMs and the template catalog (--json)
hypercluster vm createValidate a template and preview a VM create (--json)
hypercluster lab setupSecondary disk plus memory tune from the LAB_* variables
hypercluster lab storageMount the secondary disk and create the StorageClass
hypercluster lab tuneScale HA replicas down for a single-node lab (--restore)

Handoff​

CommandDescription
hypercluster forge-handoff --jsonEmit the Zynera Day-2 cluster import payload

Every command has a make equivalent — make deploy, make scale, make upgrade, make lab-setup, make dry-deploy, and so on.


Troubleshooting​

Preflight fails on Python — Kubespray v2.31 needs Python 3.11+. On macOS, /usr/bin/python3 is 3.9; run ./scripts/setup-macos.sh and confirm Homebrew Python precedes it on PATH.

python3 --version # must be 3.11+
python3 -c "import jinja2, netaddr; print('ok')"

SSH connectivity check fails — confirm the key path and that it is mode 600 (preflight fixes this for you), then test the exact hop by hand with ssh -i "$SSH_KEY" -p "${SSH_PORT:-22}" "$SSH_USER@NODE_IP" echo ok. For NAT'd guests, set SSH_PROXY_JUMP.

Passwordless sudo required (k3s path) — run the line preflight prints on each node:

echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/ubuntu-hypercluster

MetalLB IP pool is empty — deploy refuses to proceed with METALLB_ENABLED=true and no pool. Set a range on the node network that sits outside the DHCP scope, e.g. METALLB_IP_POOL="192.168.1.240-192.168.1.250".

KubeVirt VMs will not start — check /dev/kvm on the node; discover --validate reports it per host. Without it, KubeVirt needs software emulation, which preflight warns about rather than blocking.

Cilium unhealthy on a multi-node k3s cluster — CILIUM_K8S_SERVICE_HOST defaults to 127.0.0.1, which is only correct for single-node. Set it to the first control-plane IP and redeploy the stack.

A run was killed and the next one refuses to start — clear the stale lock with hypercluster unlock.

Deploy looks stuck — the Kubespray cluster.yml stage normally takes 15–30 minutes. Re-run with -v for full Ansible output.


Uninstall​

App only:

sudo rm -rf /Applications/Hypercluster.app
sudo pkgutil --forget com.zyvor.hypercluster

App and all data (or double-click Uninstall-Hypercluster.command from the distribution folder):

sudo rm -rf /Applications/Hypercluster.app
rm -rf "$HOME/Library/Application Support/com.zyvor.hypercluster"
rm -f "$HOME/Library/Preferences/com.zyvor.hypercluster.plist"
sudo pkgutil --forget com.zyvor.hypercluster

Version history​

VersionDateChanges
0.1.02026-06Initial release — Desktop app, CLI, four CNIs, AI-assisted preflight insights; the release notes list Kubespray v2.30 and Kubernetes v1.32

The sample cluster.conf on the last source commit moves the defaults to Kubespray v2.31.0, Kubernetes v1.35.4, KubeVirt v1.8.4, and CDI v1.65.1, which is what this page documents.


Suite context​

HyperCluster is the cluster foundation in the lifecycle:

Export → Convert → Inspect → … → HyperCluster (K8s) → Deploy VMs → Manage → Observe

It bootstraps the Kubernetes control plane that Zynera runs on, and the same kubeconfig serves every product above it. Pair with Zyvor Platform and h2kvm to bring workloads in; add Zeus OS and Veyron once nodes are Ready; add PacketWolf for kernel-native network intelligence on the Cilium data plane; use IronWolf to provision the metal underneath.

Zynera PacketWolf Zeus OS Veyron IronWolf

Next steps​