Harvester HCI — profile guide¶
This guide covers the four Harvester profiles in rodeo-cli. All deploy SUSE Virtualization (Harvester HCI) as nested KVM VMs on a single Linux host. Pick the one that fits your host and workshop goals.
Pick your profile¶
| Profile | Nodes | Rancher | etcd | RAM needed | Use when |
|---|---|---|---|---|---|
test |
2 Harvester | No | 1 leader | ~36 GiB | Quick evaluation, tight host, no HA needed |
harvester-ha |
3 Harvester | No | 3-member HA | ~52 GiB | Harvester workshop with full etcd HA, no Rancher |
harvester-2n |
2 Harvester + 1 Rancher | Yes | 1 leader | ~56 GiB | HCI + Rancher on a mid-size host, no etcd HA |
harvester |
3 Harvester + 1 Rancher | Yes | 3-member HA | ~60 GiB | Full lab: HCI + multi-cluster management |
Run rodeo doctor to see which profiles fit your host's available RAM.
What you get¶
All Harvester profiles¶
| Component | Default IP | Access |
|---|---|---|
| Harvester VIP | 192.168.122.10 | https://192.168.122.10 (direct, inside lab network) |
| Harvester UI via host | host IP | https://<host>:8443 (DNAT, reachable from outside) |
| harvester1 | 192.168.122.11 | rodeo ssh harvester1 |
| harvester2 | 192.168.122.12 | rodeo ssh harvester2 |
| harvester3 | 192.168.122.13 | rodeo ssh harvester3 (not present in test / harvester-2n) |
harvester and harvester-2n profiles add¶
| Component | Default IP | Access |
|---|---|---|
| Rancher Prime | 192.168.122.9 | https://<host>:30002 (DNAT) |
Host requirements¶
| Profile | RAM | Disk | CPU |
|---|---|---|---|
test |
~36 GiB | ~600 GiB in /var/lib/libvirt/images |
~16 vCPU |
harvester-ha |
~52 GiB | ~1000 GiB | ~20 vCPU |
harvester |
~72 GiB | ~1050 GiB | ~34 vCPU |
OS: Linux with KVM. SLES 16 or Leap 16 recommended; Ubuntu and Fedora work via install-deps. Nested virtualization must be enabled if the host is itself a VM (cloud, Instruqt).
Harvester installs via iPXE network boot (the pxe_server phase serves boot scripts and config over HTTP). This is why the disk requirement is high — each node gets a 320 GiB virtual disk (Harvester's Elemental installer carves a fixed 150 GiB persistent partition regardless of disk size, so the rest goes to Longhorn's own partition).
Deploy¶
rodeo up --profile test # 2-node, fastest
rodeo up --profile harvester-ha # 3-node HA, no Rancher
rodeo up --profile harvester # full lab
rodeo up checks the host, installs missing packages (with your consent), generates credentials, and drives the full pipeline. It self-escalates with sudo.
Disconnect protection (tmux): rodeo up automatically wraps itself in a named tmux session before starting the deploy. If your SSH or Instruqt connection drops mid-deploy, the process keeps running. Re-attach any time:
Detach without stopping the deploy: Ctrl+b d. If you run rodeo up again and the session exists, it re-attaches to the running deploy instead of starting a second one. Use --no-tmux to skip this behaviour in scripts.
What happens during deploy¶
The suse-virt pipeline (used by all four Harvester profiles) runs these phases:
- kvm_host — prepares the hypervisor: KVM packages, libvirt daemon, firewall rules (including the DNAT rule that makes
:8443reach the Harvester VIP), and the image storage pool - vms — downloads the Harvester ISO, creates virtual disks (size varies by profile — see Host requirements above), and writes libvirt XML definitions (VMs are defined but not started yet)
- pxe_server — sets up nginx + TFTP + dnsmasq on the host's
virbr0(192.168.122.1). Generates aboot.ipxethat chains to a per-node MAC script, and a Harvester config YAML for each node - cluster — starts VMs in order (
harvester1first, then a gap for etcd, then the rest). Waits for each node to install Harvester via iPXE, join the cluster, and becomeReady. Watches the VIP (192.168.122.10) for the cluster to converge - rancher — (
harvesterprofile only) installs K3s + Rancher Prime on therancherVM, exposes it on NodePort 30002, and configures the admin API. Harvester cluster import is intentionally left as a lab exercise — students do it via the Rancher UI as the first challenge - finalise — enables VM autostart on host reboot (skipped on Instruqt; use
rodeo start-if-neededon hostimage boot instead of baking finalise into the image)
Time estimates¶
| Profile | Typical time |
|---|---|
test |
45–90 minutes |
harvester-ha |
60–120 minutes |
harvester |
90–150 minutes |
Most of that time is the Harvester iPXE install inside nested KVM. Watch live progress:
rodeo watch # split-panel TUI: phases + serial logs
rodeo logs harvester1 # just the serial log for one node
The TUI shows all VM serial consoles simultaneously in a vertical split. The left panel has a global elapsed timer for the full deploy. Each console window shows a per-VM elapsed timer that starts from when the first serial output arrives for that node — useful for spotting a node that started late or stopped responding.
Log in¶
After deploy finishes, rodeo up prints the URLs and credentials. To see them again:
- Harvester UI:
https://<host>:8443 - Username:
admin - Password: value of
harvester_admin_passwordin~/.rodeo/secrets.yaml - Rancher UI (harvester profile):
https://<host>:30002— same password
The Harvester bootstrap process prompts for a VIP on first login if the cluster has not yet converged. If the UI asks for a VIP, use 192.168.122.10.
Instruqt labs: Harvester cluster import into Rancher is the first lab challenge. The infrastructure is ready; students complete the import via Rancher UI > Virtualization Management > Import Existing.
Download the kubeconfig¶
From the Harvester UI: Support → Download KubeConfig.
Or via SSH tunnel to the VIP:
ssh -i ~/.ssh/your-key -N -L 6443:192.168.122.10:6443 user@<host>
# then in another terminal:
kubectl --kubeconfig ~/.rodeo/harvester-kubeconfig get nodes
The kubeconfig is also at /root/.rodeo/harvester-kubeconfig on the host after deploy.
Day-2 operations¶
| Task | Command |
|---|---|
| Check cluster and VM health | rodeo status |
| Watch live logs | rodeo watch |
| SSH into a node | rodeo ssh harvester1 |
| Tail serial log | rodeo logs harvester2 |
| Restart a single node | rodeo restart harvester1 |
| Graceful stop (preserves VMs) | rodeo stop --all --yes |
| Start after stop | rodeo start --all --yes |
| Destroy the lab | rodeo clean --yes |
| Full host reset | rodeo clean --all --yes --secrets |
| Support bundle | rodeo logs --bundle -o rodeo-bundle.tar.gz |
| Rotate the admin password | rodeo set-password |
| Install/reconcile Rancher UI extensions | rodeo install-extensions |
Resume a failed deploy¶
The deploy pipeline is idempotent within a phase. If the cluster phase times out waiting for a node, check the serial log (rodeo logs harvester1) for the root cause before resuming.
Rancher UI extensions¶
Extensions (e.g. the SUSE Virtualization / Harvester extension) are declared once
under rancher.ui_extensions in definition.yaml — see the full field
reference for the format. Every
deploy reconciles the declared list automatically; to install or upgrade one on an
already-deployed lab without redeploying, run:
It talks straight to the running Rancher Prime over SSH/API, so it's safe to run against a live cluster. Check what's actually installed first:
rodeo ssh rancher
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl -n cattle-ui-plugin-system get uiplugins.catalog.cattle.io harvester \
-o jsonpath='{.spec.plugin.version}'
An empty result (or NotFound) means it isn't installed yet; a version string
that doesn't match definition.yaml means it's due for reconcile.
Instruqt workflow¶
This is the workflow for building an Instruqt hostimage with a pre-deployed Harvester cluster. Full detail: Instruqt example.
- Set
deployment_target: instruqtinrodeo-plan.yaml(skipsfinalise— keep it that way) - Deploy the lab (
rodeo up --profile harvester --yesorrodeo deploy) - Verify:
rodeo status - Before Save: do not run
rodeo deploy --finalise; confirm agent ports15778/15779and thatlibvirt-guestsis disabled (the success screen prints this checklist) - Save the hostimage in the Instruqt UI
- Wire
rodeo start-if-neededinto the track setup script so every attendee boot starts VMs and re-applies DNAT/nft
Do not bake finalise into the hostimage — nested VM autostart races the Instruqt agent and can leave the console on Please Wait.
Troubleshooting¶
Harvester VIP not reachable after cluster phase¶
The cluster phase waits for the VIP (192.168.122.10) to respond on port 443. If it times out:
rodeo logs harvester1 # check the install log — is it still installing?
rodeo ssh harvester1 # if the node is up, check: kubectl get nodes
Common causes: disk too small (Harvester's Elemental installer always carves a fixed 150 GiB persistent partition — a smaller disk starves it and causes "no space" errors in containerd), nested virt not enabled, not enough RAM.
UI reachable but host:8443 not working¶
The DNAT from host:8443 to the Harvester VIP goes through libvirt's nftables firewall. If port forwarding is broken after a virsh net-destroy/restart:
The hook is installed by the kvm_host phase. It re-runs automatically when libvirt restarts the default network.
Node never joins the cluster¶
Check the serial log for the stuck node. Common issues:
- HTTP 403 on iPXE config: nginx couldn't read the config file (permissions). Re-run
rodeo deploy --from pxe_server. - Wrong NIC name in config: Harvester config uses
hwAddr(MAC) to identify the interface, noteth0. This is handled automatically — if you see a NIC error, check the MAC indefinition.yamlmatches the VM. - etcd timeout on join: there is a built-in gap (default 60 s) between starting harvester1 and starting the others. If etcd still times out, the first node may not have finished installing — check its log.
etcd recovery (last resort)¶
If one etcd member is permanently broken (e.g. after accidentally destroying its VM), the three-step recovery is:
# 1. From a healthy node, remove the broken member
crictl exec <etcd-container-id> etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt \
--cert=/var/lib/rancher/rke2/server/tls/etcd/server-client.crt \
--key=/var/lib/rancher/rke2/server/tls/etcd/server-client.key \
member remove <member-id>
# 2. On the broken node, wipe the etcd data and add a rejoin config
rm -rf /var/lib/rancher/rke2/server/db/etcd
cat > /etc/rancher/rke2/config.yaml.d/00-rejoin.yaml <<EOF
server: https://192.168.122.10:9345
token: <cluster-token>
EOF
# 3. Restart rke2-server on the broken node
systemctl restart rke2-server
The cluster token is harvester_token in ~/.rodeo/secrets.yaml.
Admin password unknown (not in secrets.yaml, not the persisted file, not admin/admin)¶
rodeo set-password recovers the admin password by trying known candidates — the
configured secrets.yaml value, the last one it persisted on the host, and the
admin/admin bootstrap. If none of those match (e.g. someone changed it by hand
outside of rodeo-cli and forgot), there's nothing left for it to log in with, so
it can't roll the password forward. Recover directly via kubectl instead — this
is the same technique Rancher documents for "locked out, don't know the current
password," and it applies here because Harvester's dashboard embeds the same
users.management.cattle.io user-management CRD (confirmed live: cattle-system
runs a full 3-replica rancher deployment fronting the Harvester UI/API).
# 1. Find the admin user's object name
export KUBECONFIG=/root/.rodeo/harvester-kubeconfig
kubectl get users.management.cattle.io \
-o jsonpath='{range .items[*]}{.metadata.name}{" "}{.username}{"\n"}{end}'
# -> e.g. user-4wwtk admin
# 2. Generate a bcrypt hash of the new password (Go's bcrypt accepts $2a$/$2b$/$2y$ alike)
python3 -m pip install --quiet bcrypt
python3 -c "import bcrypt; print(bcrypt.hashpw(b'YourNewPassword123', bcrypt.gensalt(10)).decode())"
# 3. Patch the user object directly — sets the password hash and clears the
# "must change password" flag in one shot
kubectl patch users.management.cattle.io <user-object-name> --type=merge \
-p '{"password":"<hash-from-step-2>","mustChangePassword":false}'
# 4. Restart Rancher so all 3 replicas drop their stale in-memory cache of the
# old hash — without this, kube-vip round-robins your login attempts across
# replicas and you'll see intermittent 401s even though the patch landed in
# etcd cleanly. Only touches cattle-system — no VM/storage/network impact.
kubectl -n cattle-system rollout restart deployment/rancher
kubectl -n cattle-system rollout status deployment/rancher --timeout=180s
Verify, then bring secrets.yaml and the persisted-candidate file back in sync so
this doesn't happen again on the next rotation:
curl -sk -X POST https://192.168.122.10/v3-public/localProviders/local?action=login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"YourNewPassword123"}'
# -> a JSON body with a "token" field means it worked
# Update ~/.rodeo/secrets.yaml's harvester_admin_password to match, then:
echo -n 'YourNewPassword123' > /root/harvester-password
chmod 600 /root/harvester-password
If htpasswd (part of apache2-utils) is available instead of Python, htpasswd
-bnBC 10 "" 'YourNewPassword123' | tr -d ':\n' produces an equivalent hash.
Customize¶
Change node resources¶
Edit the profile files in ~/.rodeo/profiles/<profile>/ or override at deploy time:
Change the disk size¶
The default is 320 GiB per Harvester node. Do not go below ~250 — Harvester's Elemental installer always carves a fixed 150 GiB persistent partition regardless of disk size, so smaller disks starve it and prevent container images from pulling, and leave little room for Longhorn itself.
Build a custom Harvester lab¶
rodeo new edge-lab --from harvester-ha # scaffold from 3-node HA
$EDITOR ~/.rodeo/profiles/edge-lab/definition.yaml
rodeo up --profile edge-lab
Full format reference: Create your own rodeo.