Skip to content

Registration

Register your self-hosted cluster with the kubehz platform for dashboard visibility.

Registration and claiming are the two halves of one handshake:

  1. Register (CLI): announce the cluster to kubehz. The cluster is created with the status Creating, attached to no account yet.
  2. Claim (dashboard): prove the cluster is yours. The usual proof is the claim key that lo uploads to your Hetzner Cloud project; a claim code or a nonce the agent echoes work too. The cluster attaches to your tenant. See Claiming.

Prerequisites

  • A running Kubernetes cluster provisioned with lok8s
  • kubectl configured and connected to your cluster
  • A kubehz account (needed for the claim step, not for registration)

Configuration

Add the kubehz block to your cluster.lok8s.yaml:

yaml
spec:
  kubehz:
    # self | hosted | shared
    hosting: self
    # 'managed' unlocks the management features (Supporter+)
    access: registered
    # cronjob (read-only heartbeat) | operator (managed tier, live view)
    agent: cronjob
    apiUrl: https://api.kubehz.cloud

apiUrl must be HTTPS: the CLI refuses plain-HTTP endpoints. The optional keys (connectHcloudToken, upgrades, maintenanceWindow, space) are listed in First Cluster.

Access levels

LevelWhat kubehz seesCost
noneNothing: no connectionFree
registeredNodes, K8s version, component health, certificate expiry, uptimeFree (up to 2 clusters)
managedEverything in registered, plus the management data behind healing policies, capacity watches, and desired-state management (upgrades/scaling)Supporter tier or higher

Register

Registration runs automatically during lo provision (whenever access is not none). You can also run it manually at any time:

bash
lo kubehz register

The CLI on your machine makes the request to POST /api/clusters/register on your configured apiUrl; nothing is installed into your cluster. What it sends depends on what is in your shell:

In your shellWhat happensHow you claim
HCLOUD_TOKEN (the usual case after lo provision)The api mints a claim key and lo uploads its public half to your Hetzner Cloud project as kubehz-claim-<domain>. Re-running rotates the key.Paste the key’s fingerprint on the claim page. No token leaves your machine.
KUBEHZ_TOKEN (a clusters:write API token from the dashboard, Access → API Tokens)The cluster is registered directly on your tenant. With connectHcloudToken: true in the spec, lo also hands the platform your Hetzner token, over HTTPS only.Nothing to claim: the cluster is yours already.
neitherlo sends the domain plus the MD5 fingerprint of the SSH key in your provider descriptor.The claim code from the heartbeat agent, or the fingerprint if that key sits in your Hetzner project.

On the usual path the CLI prints the fingerprint to claim with:

kubehz: cluster 'example.com' registered (pending). Claim key 'kubehz-claim-example.com' uploaded to your Hetzner Cloud account.
kubehz: claim it with the fingerprint ALONE — dashboard /claim (SSH fingerprint tab), or:
  curl -X POST https://api.kubehz.cloud/api/claims/verify -H 'Authorization: Bearer <khzt_ token (clusters:write)>' \
       -H 'Content-Type: application/json' -d '{"fingerprint":"..."}'
  fingerprint: aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99   (also visible in Hetzner Console -> Security -> SSH keys)

The fingerprint identifies a public key. It is not a secret and reveals nothing about your private keys.

The cluster now has the status Creating: attached to no account and visible to nobody until you claim it. Unclaimed registrations are automatically deleted after 30 days; you can simply re-register at any time.

Claim your cluster

Registration alone attaches the cluster to nobody. Head to the dashboard and claim it: paste the claim key’s fingerprint, or use one of the other proofs. See Claiming for the step-by-step guide.

Heartbeats

Cluster health in the dashboard comes from an in-cluster agent in the kubehz-system namespace. With agent: cronjob (the default) it is a lightweight CronJob named kubehz-heartbeat that every 5 minutes POSTs to the kubehz API:

  • Kubernetes version
  • Node names, status, and roles
  • Control-plane component health
  • Certificate expiry

The agent runs with read-only RBAC and a hardened security context, and only makes outbound requests: the kubehz platform never connects into your cluster.

Install it with the CLI. It renders the agent manifests that ship with lok8s (the API URL, your domain and the heartbeat owner are filled in for you) and applies them with your kubeconfig:

bash
# see the rendered manifests first, apply nothing
lo kubehz deploy --dry-run

lo kubehz deploy

On its first run the agent creates its identity Secret, self-registers and starts beating. Full options: the lok8s kubehz Platform guide. Until it is installed, your cluster shows as Disconnected in the dashboard (registration and claiming still work without it).

Operator mode (managed tier)

With agent: operator, lo kubehz deploy installs the long-running kubehz-agent next to the CronJob and hands the heartbeat over to it. It watches the cluster and pushes a schema-2 payload whenever something changes. Everything it sends is metadata; workload names and contents never leave the cluster unless you opt in.

FieldWhat it carries
schema, clusterId, timestamp2, your cluster id, the time of the beat.
agentThe agent’s version and mode (operator).
kubernetes.versionThe API server’s version.
nodes[]Per node: name, status, ready, roles, instanceType, kubeletVersion, and capacity (cpu, memory).
components[]Control-plane component name and status.
workloads.podsPod counts by phase: total, running, pending, failed, succeeded, unknown. deployments (total, unavailable) when the apps informer is on.
events[]Recent Warning events: reason, kind, count, lastSeen.
actions[]Progress of desired-state actions the agent ran: type (scale, upgrade, heal), target, status, detail, revision.
machineIssues[]Machine-controller failures: pool, machine, reason, message, since.
inventoryWhat lok8s deployed, from the ClusterInventory object your own lo wrote: lok8sVersion, kind, provider, kubernetesVersion, specHash, renderedAt, and addons[] (name, chartVersion, appVersion, category, source). Never chart values or credentials.
pools[], desired, certificatesReserved for the observed worker pools, the desired-state acknowledgement and certificate expiry.

Opt-in only. Namespace names are not reported by default. Set KUBEHZ_REPORT_NAMESPACES=true on the agent to add workloads.pods.byNamespace (pod counts per namespace) and the namespace and note (the event message) fields on events. Leave it unset and those keys are absent from every beat.

The DPA refers to these fields as heartbeat metadata.

Troubleshooting

bash
# Registration status as seen by the CLI (needs KUBEHZ_TOKEN)
lo kubehz status

# Is the heartbeat CronJob in place?
kubectl -n kubehz-system get cronjob kubehz-heartbeat

# Logs from recent heartbeat runs
kubectl -n kubehz-system logs -l app=kubehz-heartbeat

# Re-run registration (safe to repeat)
lo kubehz register

# The agent lost its identity Secret (namespace recreated, cluster restored):
# mint a new one for the same registered cluster (needs KUBEHZ_TOKEN)
lo kubehz re-enroll

If the cluster is registered but missing from your dashboard, it is most likely still unclaimed; see Claiming.

Deregistration

To remove your cluster from the kubehz registry:

bash
lo kubehz deregister

This removes the cluster’s entry from kubehz and retires the claim key in your Hetzner project. Nothing inside your cluster is modified, and it continues to work independently. To remove the agent too, delete the kubehz-system namespace and its cluster-scoped RBAC (see Migration).

KUBEHZ_TOKEN

lo kubehz deregister, lo kubehz status and lo kubehz re-enroll read the tenant’s cluster registry, so they require KUBEHZ_TOKEN: a clusters:write API token minted in the dashboard under Access → API Tokens. Registration itself never needs one.

Next steps


Doc status

AspectDetail
Last reviewed5 September 2026