Skip to content
Beta

Cluster insights

Open a cluster and go to Insights to see what the platform found in it: what blocks or disrupts its next Kubernetes upgrade, what is unreliable, and where capacity is wasted or short. Every finding says where it is, how serious it is, what it means for an upgrade, and exactly what to change, as a kubectl, Helm or YAML snippet to copy. A node pool sized well above or below its use also shows the monthly saving or cost.

How it works

  • About every hour, the platform runs read-only checks inside each ready or degraded cluster: the same kind of reads kubectl get makes. They never create, change or delete anything. See What the checks read.
  • Node pool sizing comes from the workers' own CPU and memory history, the history Monitoring charts, which the platform measures from outside each worker.
  • A finding stays open while the checks keep seeing it, with when it was first and last seen. It closes on its own once it is fixed: at the next check for most kinds, and after 30 days without a call for a deprecated API.
  • The page says when the cluster was last checked. Before its first check, it says the cluster has not been checked yet. When a check cannot reach the cluster, or a part of it cannot run, the page says so and those findings stay as they were: a check that did not run proves nothing either way.

The findings are sorted worst first, in three groups: Upgrade readiness, Reliability and Efficiency.

Severity and upgrades

SeverityMeans
CriticalSomething is broken now, or breaks at the next upgrade
WarningFix it before the next upgrade, or before it causes an outage
InfoWorth knowing. It never blocks or disrupts an upgrade

Each finding also says what it means for the cluster's next Kubernetes upgrade:

  • Blocks: fix it, or accept it, before the upgrade. A minor upgrade does not start while a blocking finding is open.
  • Disrupts: the upgrade can go ahead, but causes downtime or data loss there.

A patch upgrade, such as 1.36.1 to 1.36.2, moves no pods, so only the cluster's health blocks it: workers on another version or not ready, failing system pods, and claims that are not bound. A minor upgrade, such as 1.36 to 1.37, drains workers one at a time, so removed APIs called in the last 30 days, Helm releases whose stored manifest uses removed APIs, and disruption budgets that allow nothing block it too; and it disrupts single replicas, pods without a controller, pods that keep data on their node, and admission webhooks that fail closed.

Before an upgrade starts, its confirmation says how many findings still block it, how many blockers were accepted, and how many show where it disrupts your workloads. Kubernetes upgrades explains what happens during one, and the API returns the same for either kind of upgrade: see API and MCP.

Upgrade readiness

Deprecated API calls

deprecated_api · Warning when the next minor removes it, info when a later one does · A warning blocks a minor upgrade

Something called a deprecated API version that a coming Kubernetes version stops serving, as counted by the cluster's own API server. Versions deprecated with no removal announced are not shown, since there is nothing to act on yet. The finding names the group, version and resource, the release that removes it, and its replacement. It closes once nothing has called that version for 30 days, so a nightly job that still uses it keeps it open.

Fix: find the manifests that use the old version, convert them to the replacement with the kubectl convert plugin, and update the tool, chart or controller that sends the requests:

bash
grep -rln "apiVersion: flowcontrol.apiserver.k8s.io/v1beta3" .
kubectl convert -f <file> --output-version flowcontrol.apiserver.k8s.io/v1

For a custom resource with no known replacement, the snippet shows instead which version its CustomResourceDefinition stores, the one to move to.

Helm releases with removed APIs

helm_removed_api · Critical when the cluster no longer serves one of its APIs, warning when the next minor removes one · Blocks a minor upgrade

A Helm release whose stored manifest, the one of its deployed revision, uses API versions the cluster no longer serves or the next minor removes. The next helm upgrade of that release fails until the stored manifest is fixed. The finding lists each API version and kind, and its replacement.

Fix: rewrite the stored manifest with the mapkubeapis plugin for Helm, then upgrade the release with a chart version that uses current APIs:

bash
helm mapkubeapis <release> --namespace <namespace>
helm upgrade <release> <chart> --namespace <namespace>

Disruption budgets that allow nothing

disruption_budget_blocks_drain · Warning · Blocks a minor upgrade

A PodDisruptionBudget that allows no pod to stop even though all its pods are healthy, such as minAvailable: 100% or maxUnavailable: 0. Draining a worker waits for it.

Fix: let one pod stop at a time, or run more replicas than minAvailable asks for:

bash
kubectl -n <namespace> patch poddisruptionbudget <name> --type=merge -p '{"spec":{"minAvailable":null,"maxUnavailable":1}}'

Single replicas

single_replica · Warning · Disrupts a minor upgrade

A Deployment or StatefulSet that runs one replica. When its worker is drained, it is down until it starts again on another worker. When an autoscaler scales it, the finding names the autoscaler.

Fix: run two replicas, or raise the autoscaler's minReplicas to 2:

bash
kubectl -n <namespace> scale deployment/<name> --replicas=2

Pods without a controller

pod_without_controller · Warning · Disrupts a minor upgrade

A running pod that no Deployment, StatefulSet, DaemonSet, Job or other controller owns. Nothing would start it again on another worker, so in a minor upgrade the drain of its worker waits until you delete it.

Fix: run it from a Deployment, then delete the bare pod:

bash
kubectl -n <namespace> create deployment <name> --image=<image> --replicas=2
kubectl -n <namespace> rollout status deployment/<name>
kubectl -n <namespace> delete pod <name>

Data kept on the node

local_data · Warning with a hostPath or local volume, info with emptyDir only · A warning disrupts a minor upgrade

A workload whose pods keep data on their worker: in an emptyDir or hostPath volume, or on a claim bound to a local volume. That data stays behind when the pods move to another worker. An emptyDir is scratch space that a pod starts again empty, so on its own it is only worth knowing. DaemonSets are left out: their pods do not move.

Fix: keep the data on a persistent volume claim instead. The console's snippet is a claim to start from; Kubernetes volumes explains the storage classes.

Webhooks that fail closed

webhook_fail_closed · Critical with no ready endpoint, warning with one · A critical one disrupts every upgrade, a warning a minor one

An admission webhook with failurePolicy: Fail whose Service has fewer than 2 ready endpoints. While it has none, the cluster refuses every request the webhook covers, which can stall the cluster's own services during an upgrade.

Fix: run the webhook's workload with two replicas, or set its failurePolicy to Ignore if the policy it enforces may lapse for a moment:

bash
kubectl -n <namespace> get endpointslices -l kubernetes.io/service-name=<service>
kubectl -n <namespace> scale deployment/<name> --replicas=2

Next minor not offered yet

addon_compatibility · Info · No effect on upgrades

A newer Kubernetes minor exists, but the platform has not finished testing it with the services it runs in every cluster, such as the managed gateway, so the cluster is not offered that minor yet. The finding names the minor and the minors those services are tested with. Nothing to do: the minor is offered once testing is done.

Cluster health

An upgrade starts only on a healthy cluster, so these block every upgrade, a patch included.

FindingMeansSeverityFix
Version skew (version_skew)A worker whose kubelet runs another version than the control planeCritical when the worker is newer than the control plane or 3 or more minors behind, otherwise warningCompare with kubectl get nodes -o wide and kubectl version. If it does not clear once an upgrade has finished, contact support
Worker not ready (node_not_ready)A worker not ready for more than 10 minutes, not reporting, under memory, disk or process pressure, or cordonedCritical when not ready or not reporting, warning under pressure or cordonedkubectl describe node <name> shows why, and kubectl uncordon <name> takes a worker you cordoned back. The platform replaces a worker that stays not ready
System pod failing (system_pod_failing)A pod of the cluster's own services that is crash-looping, cannot pull its image or start its container, has failed, or has been pending for more than 10 minutesWarningkubectl -n <namespace> describe pod <name> and its previous logs show why. If you did not change it and it does not recover, contact support
Claim not bound (volume_claim_pending)A persistent volume claim that a pod uses and that has been Pending for more than 10 minutesWarningkubectl -n <namespace> describe persistentvolumeclaim <name> shows its events. See A claim stays Pending

Reliability

Out of memory

oom_killed · Warning · No effect on upgrades

A container killed for running out of memory in the last 24 hours, with how many pods it hit, their restarts, when it last happened and the container's memory limit.

Fix: give it more memory. The console's snippet sets its request and limit to half again the limit it was killed at, rounded up to 64 MiB:

bash
kubectl -n <namespace> set resources deployment/<name> -c <container> --requests=memory=384Mi --limits=memory=384Mi

Efficiency

Workloads without requests

no_requests · Warning · No effect on upgrades

A workload none of whose containers request CPU or memory. The scheduler cannot plan for it, so it can crowd a worker, and it is the first to be evicted when a worker runs short of memory.

Fix: measure what it uses, then request about that:

bash
kubectl -n <namespace> top pods
kubectl -n <namespace> set resources deployment/<name> --requests=cpu=100m,memory=128Mi

Node pool sizing

pool_underused · Info · pool_overloaded · Warning · No effect on upgrades

For each node pool, the platform takes its workers' CPU and memory use over the last 7 days and works out the 95th percentile: the use the pool stays under 95% of the time.

FindingWhenSuggests
Underused poolCPU and memory both under 25%The cheapest pool that brings the busier of the two to about 60%: fewer workers of the same size, or as many of a smaller size. A pool of 2 or more workers keeps at least 2, so a drain always has somewhere to go. Shown only when it costs less, with the monthly saving
Overloaded poolCPU or memory over 85%Enough workers of the same size to bring it back to about 60%, at least one more, with the monthly cost. A pool of Free workers moves to Small
  • A pool needs at least 3 days of history, so a new pool has no finding yet.
  • The saving or cost is per month at list prices: the pay-as-you-go prices in Kubernetes pricing and usage.
  • Fix: change the pool's worker count with Edit in the cluster's Nodes section. A pool's size cannot change, so for another size add a pool of that size and delete the old one. See Kubernetes node pools.

Dismiss, snooze and accept

Some findings are deliberate: a single-replica tool you can live without for a minute, or a pool you keep large for a launch. Hide them so the page shows what is left:

  • Snooze a finding for 1, 7 or 30 days: it comes back after that.
  • Dismiss it to hide it until you restore it.
  • Restore a dismissed or snoozed finding to show it again.

A hidden finding keeps being checked and still closes once fixed. If it closes and comes back within 30 days, its dismissal still holds. Each finding shows who dismissed or snoozed it, until when, and why.

Accept a blocker

A finding that blocks the upgrade is dismissed by accepting it, and that takes a reason of 3 to 500 characters: you know about it and are upgrading anyway. It then no longer blocks: a minor upgrade can start, and its confirmation counts the finding as accepted. The reason and who gave it stay on the finding for everyone who can see the cluster, and the acceptance is recorded in the audit log. Restoring an accepted finding makes it block the upgrade again.

Dismissing, snoozing, accepting and restoring are each recorded in the audit log, and are limited to 60 changes a minute per cluster.

Who can do what

Who
See the findingsEveryone who can see the cluster: workspace owners, admins, developers and viewers, and API tokens and AI agents with kubernetes:read
Dismiss, snooze, accept and restoreWorkspace owners, admins and developers, and API tokens with kubernetes:update

Your role on the cluster itself, the one your kubeconfig uses, does not matter here: the checks run as the platform, not as you.

API and MCP

RequestDoes
GET /v1/kubernetes/clusters/{id}/insightsThe open findings, worst first, dismissed ones included, with the check's state and checkedAt
GET /v1/kubernetes/clusters/{id}/insights/upgrade?upgrade=minorFor a patch or minor upgrade (the default): blocking, the findings that block it, accepted, the blockers accepted with a reason, and disruptive, where it causes downtime or data loss
POST /v1/kubernetes/clusters/{id}/insights/{insightId}/dismissDismisses a finding: {} until restored, or { "snoozeDays": 7 } for 1, 7 or 30 days. A blocker needs a reason, or the answer is 400 K8S_INSIGHT_REASON_REQUIRED
DELETE /v1/kubernetes/clusters/{id}/insights/{insightId}/dismissRestores it

Starting a minor upgrade with POST /v1/kubernetes/clusters/{id}/upgrades while a finding blocks it answers 409 K8S_UPGRADE_BLOCKED.

state is pending before the first check, failed when the last check could not reach the cluster (the findings are those of checkedAt), partial when parts of it could not run (listed in unchecked), and otherwise ok. Each finding looks like this:

json
{
  "id": "kin_3f9a1c0b7d2e4a6f8b10",
  "kind": "single_replica",
  "category": "readiness",
  "severity": "warning",
  "upgradeImpact": "disrupts",
  "details": { "namespace": "shop", "kind": "Deployment", "name": "web", "autoscaler": null },
  "fix": { "language": "shell", "code": "kubectl -n shop scale deployment/web --replicas=2" },
  "cost": null,
  "firstSeenAt": "2026-10-10T09:00:00.000Z",
  "lastSeenAt": "2026-10-16T11:00:00.000Z",
  "dismissed": null,
  "dismissedUntil": null,
  "dismissedBy": null,
  "dismissReason": null
}

upgradeImpact is the finding's effect on a minor upgrade, the widest case. cost, for a node pool, is { "kind": "saving", "monthlyUsd": 10.22 } or { "kind": "cost", … }, with the suggested size in details.suggestion. dismissed is snoozed, forever or null.

AI agents connected over MCP read the same findings with list_kubernetes_cluster_insights, and can act on a pool's suggestion with scale_kubernetes_node_pool. The tool only reads: dismissing and accepting stay with people in the console and with API tokens.

What the checks read

  • Reads only. The checks run on the cluster's control plane and only list and read objects, as kubectl get does. They change nothing in the cluster.
  • Names, never values. A finding keeps names, counts, versions and states: namespaces, object and container names, images, replica and restart counts, API versions. The value of a Secret or of an environment variable is never collected.
  • Helm releases are stored in the cluster as Secrets. The check opens the deployed revision of each release on the control plane and keeps only the API version and kind of each object in its manifest: never its values.
  • The cluster's own namespaces, such as kube-system and the managed gateway's, are left out, except to tell whether the cluster's own services are running.
  • Your workspace's findings are shown only to its members, API tokens and agents, as above. Our support team can see the same findings, read only, to help with your cluster.

Limits

How oftenAbout every hour, for a ready or degraded cluster
FindingsUp to 50 of each kind per check; fix the ones shown and the next check shows the rest
Closed findingsDeleted 30 days after they close
Dismiss, snooze, accept and restore60 changes a minute, per cluster