Skip to main content

Harvester

:::tip Complete ✅ Three-node Harvester v1.7.1-govt.2 cluster running on nuc-01/02/03. Post-install bootstrap (namespaces, CA injection, LoadBalancers) still pending — see Post-Install Bootstrap below. :::


Bare-Metal Install

Overview

Three Intel NUC nodes (nuc-01, nuc-02, nuc-03) form a Harvester HCI cluster via network boot. nuc-01 creates the cluster and waits for the VIP to become reachable before nuc-02 and nuc-03 join. The entire install is unattended: iPXE fetches the Harvester ISO and a per-node YAML config from nuc-00's web server, and the nodes bootstrap themselves.

The cluster VIP is 10.0.0.100. Once it is reachable over HTTPS, the cluster is up.

Prerequisites

Before booting any Harvester node:

  • Hauler store loaded and hauler store serve registry running on nuc-00 port 5000 (Harvester pulls its container images from here during install)
  • Harvester iPXE assets unpacked at /srv/www/htdocs/harvester/v1.7.1-amd64-govt.2/
  • Per-node config YAML files generated and placed at /srv/www/htdocs/harvester/harvester/
  • DHCP host entries configured in /etc/dhcpd.conf for the MAC addresses of nuc-01, nuc-02, and nuc-03 so each gets a predictable IP
caution

Do not boot nuc-02 or nuc-03 before nuc-01's cluster creation is complete and the VIP 10.0.0.100 is reachable. Joining nodes that find no cluster to join enter a broken state.

Install procedure

Step 1 — Boot nuc-01 (create cluster)

Power on nuc-01 and select network boot (F12 on NUC). The iPXE menu appears and either auto-selects or offers the option Deploy Harvester to nuc-01 (create cluster). The node installs Harvester, writes the cluster config, and reboots. This takes approximately 10–15 minutes.

Step 2 — Wait for the cluster VIP

From nuc-00, poll until the API is reachable:

until curl -sk https://10.0.0.100/ping | grep -q "pong"; do
echo "Waiting for Harvester VIP..."; sleep 15
done
echo "Harvester cluster is up"

Do not proceed until this returns successfully.

Step 3 — Boot nuc-02 (join)

Power on nuc-02 and select network boot. The iPXE menu selects Join nuc-02 to cluster. The node installs and joins. Wait for the node to appear Ready in the Harvester UI (https://10.0.0.100 → Hosts).

Step 4 — Boot nuc-03 (join)

Repeat for nuc-03. Once all three nodes show Ready, bare-metal install is complete.

Regenerating configs

If node configs need to be regenerated (e.g., after a MAC address change or config correction):

bash /srv/www/htdocs/carbide-enclave.kubernerdes.com/infra/nuc-00/srv/www/htdocs/harvester/harvester/generate-harvester-configs.sh

The script writes new YAML files to /srv/www/htdocs/harvester/harvester/ and can be re-run safely. Config files are served over HTTP — no restart of any service is needed after regeneration.


Post-Install Bootstrap

After the three-node cluster is up and you have the Harvester kubeconfig, run 30_bootstrap-harvester.sh to configure namespaces, inject the internal CA certificate, and apply the three service LoadBalancers.

Prerequisites

  • Harvester cluster formed and reachable at https://10.0.0.100
  • Harvester kubeconfig downloaded to ~/.kube/carbide-enclave-harvester.kubeconfig (Harvester UI: Support → Download KubeConfig, or copy /etc/rancher/rke2/rke2.yaml from nuc-01)
  • step-ca running on nuc-00 with the root certificate served at http://10.0.0.10/step/carbide-enclave-root-ca.crt
  • kubectl in PATH on nuc-00

Run the script

KUBECONFIG=~/.kube/carbide-enclave-harvester.kubeconfig \
bash /srv/www/htdocs/carbide-enclave.kubernerdes.com/scripts/30_bootstrap-harvester.sh

The script is idempotent — safe to re-run if a step fails partway through.

What it does

1. VM namespaces

Creates three namespaces that map to workload tiers. These appear as Projects in the Harvester UI and are where OpenTofu places VMs.

NamespaceWorkload
vms-rancherRKE2 management cluster VMs (rancher-01/02/03)
vms-observabilityObservability stack VMs (Grafana, Prometheus)
vms-appsGeneral application VMs

2. Internal CA injection

Patches the additional-ca cluster setting (settings.harvesterhci.io) with the step-ca root certificate. Harvester's controller distributes this cert to every node's system trust store. Without this, Harvester cannot verify the TLS certificate served by Harbor, causing image pulls to fail with certificate errors.

The root certificate is fetched from the bastion at runtime:

http://10.0.0.10/step/carbide-enclave-root-ca.crt

3. LoadBalancers

Applies IPPool and LoadBalancer resources that give each service tier a pinned VIP. Harvester's built-in load balancer controller (backed by kube-vip) manages these.

LB nameVIPPortsBackend selector
rancher-api10.0.0.306443, 9345, 80, 443tag.harvesterhci.io/role: rke2-server
observability-ingress10.0.0.4080, 443harvesterhci.io/vmNamePrefix: observability
apps-ingress10.0.0.5080, 443harvesterhci.io/vmNamePrefix: apps

Source manifests: infra/harvester/lb-apps.yaml, infra/harvester/lb-observability.yaml (the rancher-api LB manifest was applied in an earlier step).

Backend selector: labeling VMs

Harvester's LB controller matches backend VMs by comparing the backendServerSelector labels against labels on VirtualMachine objects in the same namespace.

For vms-rancher, the RKE2 VMs must carry the tag role: rke2-server. Set it in OpenTofu:

resource "harvester_virtualmachine" "rancher" {
# ...
labels = {
"tag.harvesterhci.io/role" = "rke2-server"
}
}

For vms-observability and vms-apps:

harvesterhci.io/vmNamePrefix: observability # or: apps
note

The harvesterhci.io/vmNamePrefix label is not automatically set based on the VM name — it must be applied explicitly on each VM via OpenTofu or the Harvester UI. Alternatively, replace it with a tag.harvesterhci.io/ label and update the backendServerSelector in the manifest to match.

Verify

After the script completes, confirm all resources are in place:

export KUBECONFIG=~/.kube/carbide-enclave-harvester.kubeconfig

# LoadBalancer VIPs — look for ALLOCATED-ADDRESS = 10.0.0.30 / .40 / .50
kubectl get loadbalancers.loadbalancer.harvesterhci.io -A

# IP pools
kubectl get ippools.loadbalancer.harvesterhci.io

# Namespaces
kubectl get namespaces vms-rancher vms-observability vms-apps

# CA cert injected — should print subject and validity dates
kubectl get settings.harvesterhci.io additional-ca \
-o jsonpath='{.value}' \
| openssl x509 -noout -subject -issuer -dates

All four commands should return clean output with no errors before proceeding.


Next step

Provision the RKE2 management cluster VMs via OpenTofu: RKE2 Cluster