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 way | The HyperCluster way |
|---|---|
Hand-write hosts.yaml and group_vars per cluster | One cluster.conf generates the full Kubespray inventory and group_vars |
Discover mid-deploy that a key is wrong or python3 is 3.9 | preflight checks local tooling, SSH reachability, sudo, and /dev/kvm up front |
scp admin.conf off a control-plane node by hand | fetch-kubeconfig / merge-kubeconfig land it where kubectl expects it |
| A lab cluster still needs a day of Helm to run VMs | The k3s path installs Cilium, MetalLB, cert-manager, KubeVirt, and CDI in order |
| Two operators deploy the same cluster at once | Per-cluster locking on every mutating command, with unlock for stale locks |
| "Did it actually work?" answered by reading pod logs | test, health, and status return structured pass/fail with --json |
At a glance
| Area | Detail |
|---|---|
| Engine | Kubespray v2.31.0 (Ansible) · k3s · RKE2 · MicroK8s |
| Kubernetes | v1.35.4 default pin (KUBE_VERSION, any Kubespray-supported tag) |
| Config | A single cluster.conf → Kubespray inventory + group_vars + kubeconfig |
| Transport | Pure SSH, agentless — no cloud API, no node daemon |
| Runtime | Podman default (containerd, docker supported) |
| CNI | Cilium default; Calico, Flannel, kube-ovn on the Kubespray path |
| Virtualization | KubeVirt v1.8.4 + CDI v1.65.1, with CNAO, SSP, KubeVirt CSI, snapshot-controller |
| Discovery | 5 sources — libvirt, Proxmox, VMware, SSH CIDR scan, CSV |
| Day-2 | scale, upgrade, remove-node, destroy, health, test |
| CLI | 22 commands · --json on every read path |
| Desktop | Native Cluster Studio (Tauri) driving the same CLI · macOS 13 Ventura+ on Apple Silicon (M1+) · 30-day free trial |
| Version | 0.1.0 (initial release, 2026-06) — Desktop app and CLI |
| Licence | Proprietary, all rights reserved; per-seat and team plans |
| Handoff | forge-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.
| Engine | How it provisions |
|---|---|
kubespray (alias kubeadm) | Default. Upstream HA Kubernetes via pinned Kubespray Ansible playbooks over SSH |
k3s | Single-binary k3s installed over SSH, followed by the full Helm platform stack |
rke2 | Rancher's hardened distribution — rke2-server on control planes, agents joined by node-token |
microk8s | Snap install plus microk8s add-node join for Ubuntu fleets |
talos | Immutable 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 fromCONTROL_PLANE_IPS/WORKER_IPSgroup_vars/k8s_cluster/k8s-cluster.yml—kube_version,container_manager,kube_network_plugin, pod/service CIDRs,kubelet_cgroup_driver: systemd(required for rootful Podman), andkubeconfig_localhost: truegroup_vars/k8s_cluster/addons.yml—metrics_server_enabledgroup_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:
| Command | Playbook |
|---|---|
deploy | cluster.yml (typically 15–30 minutes) |
scale | scale.yml |
upgrade | upgrade_cluster.yml |
remove-node | remove_node.yml with reset_nodes=true |
destroy | reset.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:
- Cilium — eBPF CNI, kube-proxy replacement, with a pod-API verification gate
- cert-manager — in-cluster TLS, ready before any webhook that needs it
- MetalLB — L2 address pool from
METALLB_IP_POOLsoLoadBalancerServices get real IPs - snapshot-controller — plus a default
VolumeSnapshotClass - Storage — a default
local-pathStorageClass when the cluster has none - metrics-server — host-network tuned, so
kubectl topworks immediately - KubeVirt operator → CDI → disks-images-provider → KubeVirt CSI
- CNAO — Multus and multi-network attachments for VMs
- SSP — VM templates, with webhook TLS and CA-bundle patching handled automatically
- Optional — Longhorn, Descheduler, Node Feature Discovery, NVIDIA GPU Operator
- 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:
| Variable | Purpose |
|---|---|
CILIUM_KUBE_PROXY_REPLACEMENT | Cilium replaces kube-proxy entirely |
CILIUM_HUBBLE_ENABLED | Flow observability |
CILIUM_BGP_ENABLED | BGP control plane for route advertisement |
CILIUM_WIREGUARD_ENABLED | Transparent node-to-node encryption |
CILIUM_GATEWAY_API_ENABLED | Gateway API support |
CILIUM_HOST_FIREWALL_ENABLED | Host-level network policy |
CILIUM_K8S_SERVICE_HOST | API 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
--probeSSHes each host and reports vCPU, memory, storage, GPU presence, NIC count, and NUMA layout.--validatechecks/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, plusansible/ansible-playbook/python3on the Kubespray path orhelmon 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.dline to fix it - MetalLB IP pool — refuses to proceed if
METALLB_ENABLED=trueandMETALLB_IP_POOLis empty /dev/kvmon 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
IPAddressPoolpresent - KubeVirt Available, CDI Available
VirtualMachineSnapshotandVolumeSnapshotAPIs present- metrics-server answering
kubectl top nodes - Longhorn pods Running (when enabled)
- A live cirros VM smoke test
- Remote
kubectlover 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.
| Variable | What it solves |
|---|---|
NODE_SSH_OVERRIDES_JSON | Per-node user, key, or port for mixed-vintage hardware that shares no login convention |
SSH_PROXY_JUMP | Reach NAT'd libvirt guests through their hypervisor as a jump host |
K3S_API_LOCAL_PORT | Rewrite 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_ARGS | Any 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.conffrom 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 --jsonoutput. - Live deploy console — streams stdout and stderr with parsed deploy stages and a cancel that sends
SIGTERMto the running subprocess. - Command palette and editor —
⌘Kruns any command; a Monaco editor editscluster.confand offers to run preflight on save. - AI Copilot —
⌘Jfor fleet insights, with optional LLM chat that answers in HyperCluster commands rather than rawkubectl.
Security model
| Property | Detail |
|---|---|
| Node access | SSH only — no persistent agent is installed on the nodes |
| Credentials | SSH keys stay on your Mac; no key material is sent off-device |
| kubeconfig | Fetched directly to the local kubeconfig path over SSH |
| Telemetry | No analytics and no update checks |
System requirements
Deployer machine (laptop or bastion)
| Tool | Minimum | Notes |
|---|---|---|
| Bash | 4.0+ | macOS ships 3.x — brew install bash |
| Python | 3.11+ | Required by Kubespray v2.31 / Ansible 11 |
| Ansible | 2.14+ (11.x for Kubespray v2.31+) | Kubespray path only |
| Helm | 3.x | k3s platform-stack path only |
| kubectl | Matching the cluster version | |
| Git | Any | Used to clone Kubespray at the pinned tag |
HyperCluster Desktop
| Requirement | Detail |
|---|---|
| macOS | 13 Ventura or later, Apple Silicon (M1+) |
| Disk | About 50 MB installed |
| Access | Passwordless SSH from the Mac to each node |
| Network | Internet is needed during deploy, because Kubernetes images are pulled to the nodes |
| Runtime | The 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
| Requirement | Detail |
|---|---|
| OS | Linux, SSH-reachable from the deployer |
| Access | SSH key auth; passwordless sudo (required on the k3s path) |
/dev/kvm | On the control plane when KubeVirt is enabled |
| Secondary disk | Optional — LAB_SECONDARY_DISK for VM PVCs on single-node labs |
| Networking | Pod 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
- Download
Hypercluster-0.1.0.pkgfrom the release page. - Open the
.pkg, then choose Continue, Agree, Install and enter your Mac password when prompted. - 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
| Command | Description |
|---|---|
hypercluster wizard | Interactive cluster.conf generator — no existing config needed |
hypercluster init | Clone Kubespray and generate inventory plus group_vars |
hypercluster init --wizard | Generate the config interactively first, then init |
hypercluster init --profile NAME | Start from config/profiles/NAME.conf.example |
hypercluster preflight | Verify local tools and SSH connectivity to every node (--json) |
Lifecycle
| Command | Description |
|---|---|
hypercluster deploy | Full deployment — preflight, init, cluster bootstrap |
hypercluster -n deploy | Dry run — print the plan, change nothing |
hypercluster scale | Add the worker nodes listed in cluster.conf |
hypercluster upgrade | Upgrade Kubernetes to KUBE_VERSION |
hypercluster remove-node NAME | Drain and cleanly remove a single node |
hypercluster destroy | Tear down the cluster (confirmation required) |
hypercluster unlock | Clear a stale per-cluster lock after a crash |
Access
| Command | Description |
|---|---|
hypercluster fetch-kubeconfig | Copy admin.conf from the first control-plane node |
hypercluster merge-kubeconfig | Merge the cluster kubeconfig into ~/.kube/config |
Discovery and inspection
| Command | Description |
|---|---|
hypercluster discover --source SRC | Enumerate nodes from libvirt, Proxmox, VMware, ssh-scan, or csv |
hypercluster discover --probe | SSH into each host and report hardware |
hypercluster discover --validate | Check virtualization readiness per host |
hypercluster probe-nodes | Reachability check without a cluster.conf |
hypercluster status | Node readiness and roles (--json) |
hypercluster health | Nodes, pods, services, VMs — one verdict (--json) |
hypercluster health --with-test | Health plus the k3s platform smoke pass |
hypercluster workloads | List pods and services (--json) |
hypercluster test | Platform smoke tests (--full, --json) |
Virtualization and lab
| Command | Description |
|---|---|
hypercluster install-kubevirt | Install the KubeVirt operator and CDI |
hypercluster vms | List KubeVirt VMs and the template catalog (--json) |
hypercluster vm create | Validate a template and preview a VM create (--json) |
hypercluster lab setup | Secondary disk plus memory tune from the LAB_* variables |
hypercluster lab storage | Mount the secondary disk and create the StorageClass |
hypercluster lab tune | Scale HA replicas down for a single-node lab (--restore) |
Handoff
| Command | Description |
|---|---|
hypercluster forge-handoff --json | Emit 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
| Version | Date | Changes |
|---|---|---|
| 0.1.0 | 2026-06 | Initial 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 IronWolfNext steps
- Blog: Kubernetes lifecycle with Kubespray · Installing KubeVirt on bare metal · Edge + K3s stack · Kubernetes universal control plane
- Deployment · Product suite
- Contact sales for Enterprise SLAs