Skip to main content
This guide takes you from a fresh Kubernetes cluster to a registered, online Operator executor. End-state: a single nvcl-operator Deployment runs in the nvcl-system namespace, the Hub sees the executor as online, and you can deploy nodes from the UI. The Operator is the Kubernetes executor: it runs each node as a StatefulSet of pods and reports status back to the Hub. Choose it when you already run Kubernetes, want pod-level isolation, or plan to scale fleets. For the executor concept and the Agent-vs-Operator choice, see Executors.

Prerequisites

  • Kubernetes — a cluster serving networking.k8s.io/v1 (Ingress, NetworkPolicy). The chart declares no kubeVersion floor.
  • Cluster-admin at install time — the chart creates a namespaced Role/RoleBinding and, for cluster telemetry, a small cluster-scoped ClusterRole that reads node capacity.
  • A storage class suitable for blockchain workloads — typically a CSI provider with WaitForFirstConsumer binding, fast NVMe-backed for heavy chains.
  • Outbound HTTPS from the Operator pod to the Hub (https://hub.novacula.io). No inbound ports are needed.
  • An executor API key, created via Connect Executor (Operator kind) — see Connect an executor.
  • (Optional) cert-manager with a ClusterIssuer — only if you’ll expose node RPC via proxy with automatic TLS. See RPC exposure.

Steps

1

Run the Helm command

From the Connect Executor screen in the UI, copy the exact Helm command. There’s no helm repo add — the chart is an OCI ref. It installs the Operator in hub mode (it syncs to the Hub each tick) and renders the Hub token into a Secret. It looks like this:
  • image.tag pins the Operator version; gateway.tag pins the matching gateway image (same version), which runs both as each pool’s own gateway and as the per-node RPC-auth sidecar.
  • hub.url and hub.token are the Hub connection; hub.name is the executor’s identity in the Hub (defaults to the Helm release name).
  • --set source=crd switches to standalone/GitOps mode (reconcile Node CRs in-namespace, no Hub); the default is hub.
The chart deploys, all in the release namespace:
  • A single-replica Deployment nvcl-operator (+ ServiceAccount), running the operator with run --config /etc/nvcl/operator.toml.
  • A Secret nvcl-operator-config holding operator.toml (with the Hub token) — mounted at /etc/nvcl/operator.toml.
  • A namespaced Role + RoleBinding nvcl-operator covering statefulsets, configmaps/secrets/services/persistentvolumeclaims, ingresses/networkpolicies, deployments (get, list, create, patch, delete — each pool’s gateway runs as a Deployment of its own), get on replicasets (to find its own Deployment), read-only pods/pods/log, metrics.k8s.io pods (live usage of its own pod and the node pods it manages), and novacula.io nodes (+ /status); with self-update on, it also patches this Deployment (via resourceNames) and runs the chart-upgrade Jobs.
  • A small cluster-scoped ClusterRole + binding that reads node capacity and allocatable for cluster telemetry (disable with --set clusterNodeRead=false) — see Cluster telemetry.
2

Confirm online in the UI

The first log lines show the Operator loading its config, syncing with the Hub, and reporting its capabilities. The executor row in the UI flips to online within seconds.If it stays offline:
  • Check Operator logs for auth errors.
  • Confirm outbound HTTPS to the Hub URL.
  • Verify hub.token is the exc_ key from the Connect Executor step.

What lives in the cluster

Static (chart-managed, in the release namespace):
Dynamic (Operator-managed, per deployed node):
The Operator manages these with server-side apply; deleting a node removes its objects (including its persistentvolumeclaims). Dynamic (Operator-managed, per pool — <pool> is nvcl-pool-<pool id>):
The Operator pushes each pool’s config to its gateway and waits for the gateway to acknowledge it. The gateway serves the pool from its own pod, dialing members through their headless Services, so a member restarting, stopping, or being removed never takes the pool’s address down with it. Once this executor no longer serves a pool, the Operator sweeps that pool’s objects.

Exposing node RPC

Each node’s RPC exposure is set per node (see RPC exposure); the Operator materializes it:
  • Direct → a NodePort or LoadBalancer Service (your choice).
  • Proxy → an Ingress at your domain, with TLS via cert-manager when you set a ClusterIssuer, and optional RPC-key auth through the node’s gateway sidecar.
  • Closed → internal ClusterIP only.
For proxy TLS, set the cert-manager ClusterIssuer and IngressClass via the chart’s proxy.* values (see below).

Operating

The Operator’s own upgrade is driven from the UI/Hub. A release that needs only a new image makes the Operator patch its own Deployment. A release that needs a newer chart can’t be requested by hand from the console — the upgrade dialog shows the helm upgrade command to run yourself instead. When such an upgrade reaches the Operator another way (an automatic update, for instance), the Operator applies the chart with a Helm Job if selfUpdate.enabled is on (the default; the Job uses selfUpdate.helmImage, alpine/helm:4.2.3). With selfUpdate.enabled: false the Operator doesn’t update itself at all, so run helm upgrade yourself; see Operator chart upgrades. Otherwise, run helm upgrade only for your own changes (resources, values).
Release 0.1.18 needs chart 0.1.13. Its RBAC adds deployments for the per-pool gateway (and drops patch on pods and pods/status). An Operator that gets the 0.1.18 image without the chart upgrade can’t create any pool’s gateway, so its pools answer nowhere. When 0.1.18 arrives as an automatic update and self-update is on, the Operator runs the chart upgrade itself. Otherwise — including a manual upgrade from the console — run helm upgrade with --version 0.1.13.

Configuration (Helm values)

The chart composes operator.toml from its values and renders it into the Secret. Common values:
  • The namespace the Operator watches and deploys nodes into is the Helm release namespace (--namespace).
  • The gateway.* limits bound every pool gateway and node sidecar; they must be whole numbers, and they can’t be set through extraConfig. Changing one restarts node pods and recreates every pool gateway — a brief outage per pool. See Gateway limits.
  • Per-node settings (resources, config/CLI overrides, RPC exposure) aren’t Helm values — set them per node in the UI. See Deploy a node and Edit a running node.

Cluster telemetry

The Operator reports cluster capacity from two reads, and they’re scoped as narrowly as the figures allow: Setting clusterNodeRead: false drops the cluster-wide read: node count, CPU capacity, and allocatable memory are then omitted. Workload usage is unaffected — it never leaves the release namespace, so the Operator’s ServiceAccount is never granted a pod list across your cluster. If you’re moving from an older chart, run helm upgrade to pick up the current RBAC — memory reporting on the executor’s Overview and Monitoring tabs depends on it. See Node monitoring.

Hardening

  • Keep the Hub token secret — it lives in the nvcl-operator-config Secret; restrict who can read Secrets in nvcl-system.
  • Let the Hub own the image when self-update is on — pinning image.tag and then helm upgrade --reuse-values re-applies the pin and can undo a self-update. Either let self-update own the image, or manage it only via Helm.
  • Fence the pool gateways’ admin port to the Operator pod — the port the Operator pushes pool config to. The gateway already refuses a push without the Operator’s credential; for defence in depth (it needs a CNI that enforces NetworkPolicy), add to extraConfig:
    The Operator then writes a NetworkPolicy per pool that lets only its own pod reach the admin port, and leaves the pool’s serving port open. It’s off by default.
  • Restrict egress from nvcl-system to the Hub URL (and per-chain image/binary sources) if you run NetworkPolicies.

Next steps