Registration
Register your self-hosted cluster with the kubehz platform for dashboard visibility.
Registration and claiming are the two halves of one handshake:
- Register (CLI): announce the cluster to kubehz. The cluster is created with the status Creating, attached to no account yet.
- Claim (dashboard): prove the cluster is yours. The usual proof is the claim key that
louploads 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
kubectlconfigured 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:
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.cloudapiUrl must be HTTPS: the CLI refuses plain-HTTP endpoints. The optional keys (connectHcloudToken, upgrades, maintenanceWindow, space) are listed in First Cluster.
Access levels
| Level | What kubehz sees | Cost |
|---|---|---|
none | Nothing: no connection | Free |
registered | Nodes, K8s version, component health, certificate expiry, uptime | Free (up to 2 clusters) |
managed | Everything 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:
lo kubehz registerThe 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 shell | What happens | How 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. |
| neither | lo 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:
# see the rendered manifests first, apply nothing
lo kubehz deploy --dry-run
lo kubehz deployOn 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.
| Field | What it carries |
|---|---|
schema, clusterId, timestamp | 2, your cluster id, the time of the beat. |
agent | The agent’s version and mode (operator). |
kubernetes.version | The 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.pods | Pod 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. |
inventory | What 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, certificates | Reserved 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
# 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-enrollIf 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:
lo kubehz deregisterThis 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
- Claiming: prove ownership and attach the cluster to your account
- Dashboard: using the dashboard
- KubeOne: provisioner guide
- Hosted setup: hosted control plane alternative
Doc status
| Aspect | Detail |
|---|---|
| Last reviewed | 5 September 2026 |