Skip to main content
Version: 2.0

Nodes Manager

Service: nodes-manager · Port: 8115 · Database schema: nodes

nodes-manager looks after the nodes your workloads run on. Working with Karpenter, it keeps the right amount and the right kind of capacity: it places workloads where they fit best, takes advantage of cheap spot capacity safely, consolidates underused nodes, and repairs node states that would otherwise need a person.

What it does continuously​

Every scan cycle, nodes-manager:

  1. Syncs node state: every node's status, capacity, pods and pricing, for the dashboard and API.
  2. Scores placement: classifies each workload and checks it's on a suitable node (see below).
  3. Plans consolidation: finds underused nodes that can be emptied safely and, when enabled, consolidates them.
  4. Repairs stale cordons: uncordons nodes left cordoned by spot rebalance warnings that never became interruptions.
  5. Publishes events: every scaling, consolidation and repair action goes to nodes.events.

Workload placement​

Each pod is classified into a workload class (latency_sensitive, gpu, batch, stateful or stateless) from its QoS class, GPU requests, owning controller and volumes. Candidate nodes are then scored on five weighted dimensions:

DimensionWeightFavors
Resource fit35%Nodes where the pod fits without waste.
Topology spread25%Spreading replicas across zones.
Cost efficiency20%Spot over on-demand where appropriate.
Interruption risk15%Stable capacity for sensitive workloads.
Data locality5%Nodes near the pod's volumes.

Each decision comes with a plain-language rationale, so you can see why a workload belongs where it does. nodes-manager feeds these preferences to Karpenter as node pool requirements and pod affinities.

Node pools​

nodes-manager manages Karpenter on every cluster Tantilium Cloud Runner (TCR) knows about: the host cluster, plus each cluster registered in TCR. It reads those clusters from the same registry the workflow engine uses. Pick a cluster on the dashboard's Node Pools page (super admins only), then:

  • Node pools. Create, edit and delete Karpenter NodePools for different workload classes, such as a spot pool for batch work and an on-demand pool for latency-sensitive services. You can set:
    • instance requirements
    • taints and labels
    • weight
    • capacity limits
    • consolidation policy
    • disruption budgets, including scheduled maintenance windows
  • Nodes. See each pool's NodeClaims with their phase, instance type, capacity type, zone, and drift or consolidation signals. Replace a node to have Karpenter drain it and launch a fresh one.
  • Node classes. Create, edit and delete the provider's NodeClass, which holds the image, network and identity settings for a pool's nodes.

The feature isn't tied to one cloud. Karpenter's karpenter.sh/v1 API is the same on every provider. nodes-manager discovers each cluster's NodeClass kinds at runtime, such as EC2NodeClass on EKS or AKSNodeClass on AKS with Node Auto Provisioning. A NodeClass spec is edited as JSON and validated by the provider's own schema.

A cluster without Karpenter shows as such. Nothing is installed for you.

Objects that Flux, Argo CD or Helm manage are flagged, because an edit made here would be reverted on their next sync.

Spot capacity​

The spot market advisor reads current spot prices and interruption frequency per instance type and zone (cached for five minutes per region), so pools use the cheapest capacity that's reliable enough for their workloads.

When AWS issues a spot rebalance recommendation, the node termination handler cordons the node as a precaution. Often no interruption follows. nodes-manager watches for nodes that are still Ready, carry only the plain unschedulable taint and have stayed that way past a grace period (15 minutes by default, well beyond a real interruption's two-minute notice), and uncordons them. Manual maintenance cordons and Karpenter's own disruption taints are never touched.

Consolidation​

nodes-manager continuously looks for underused nodes whose pods can move elsewhere without breaking any PodDisruptionBudget, and estimates the savings. When consolidation is enabled, it drains and removes them through Karpenter. Review the current plan on the Node Pools page or with GET /api/v1/optimize/plan.

REST API​

MethodPathDescription
GET/api/v1/nodesNodes with state, resources and pod counts.
GET/api/v1/nodes/{id}Node detail with its pods.
POST/api/v1/nodes/{id}/drainDrain a node safely.
GET/api/v1/decisionsPlacement decisions with scores and rationale.
GET/api/v1/workloads/placementEvery workload's node, class, score and rationale.
GET/api/v1/optimize/planConsolidation plan with savings and PDB impact.
POST/api/v1/optimize/triggerRun an optimization cycle now.
POST/api/v1/simulateSimulate scheduling N pods and see the nodes needed.
GET/api/v1/karpenter/clustersEvery cluster with its Karpenter status and NodeClass kinds (?refresh=1 re-discovers).
GET · POST/api/v1/karpenter/clusters/{cluster}/nodepoolsList or create node pools.
GET · PUT · DELETE/api/v1/karpenter/clusters/{cluster}/nodepools/{name}Node pool detail, update, delete.
GET/api/v1/karpenter/clusters/{cluster}/nodeclaimsNodes Karpenter launched (?nodepool=).
DELETE/api/v1/karpenter/clusters/{cluster}/nodeclaims/{name}Drain and replace one node.
GET · POST/api/v1/karpenter/clusters/{cluster}/nodeclassesList or create provider NodeClasses.
GET · PUT · DELETE/api/v1/karpenter/clusters/{cluster}/nodeclasses/{kind}/{name}NodeClass detail, update, delete.
GET/api/v1/spot/marketSpot prices and interruption data (?region=).
GET/healthzHealth check.

The /api/v1/karpenter routes require a super admin token. {cluster} is a registered cluster's id, or host for the cluster nodes-manager runs in.

Configuration​

VariableDefaultDescription
SCAN_INTERVAL_SECONDS60Time between scan cycles.
CONSOLIDATION_ENABLEDtrueConsolidate underused nodes automatically.
AUTO_UNCORDON_ENABLEDtrueRepair stale spot-rebalance cordons.
AUTO_UNCORDON_GRACE_PERIOD_MINUTES15How long a node must stay cordoned before it's repaired.
AWS_REGION—Region for spot pricing.
DATABASE_URL—PostgreSQL connection.
RABBITMQ_URL—RabbitMQ connection.
AUTH_JWT_ACCESS_SECRET—Validates bearer tokens.
APP_PORT8115HTTP port.
KUBEOPERA_API_BASE_URL—kubeopera-api, for the cluster registry. Unset: only the host cluster is managed.
KUBEOPERA_API_INTERNAL_KEY—Key for kubeopera-api's internal cluster registry endpoint.
CLUSTER_SECRETS_NAMESPACEkubeopera-systemWhere registered clusters' kubeconfig Secrets live.

AWS access uses the service's workload identity (IRSA), so there are no static keys.