Observing Karpenter through Prometheus: node and pod metrics, scheduling latency, and provisioning metrics. Complete with a Grafana dashboard and alerting for pods stuck in Pending and effective consolidation.

In episode 11 you learned about drift detection and how Karpenter keeps nodes aligned with the NodeClass and NodePool templates. But there is one question that always surfaces once an automated system is running: how do we know everything is working correctly? An autoscaler that is not observed is a ticking time bomb. Every provisioning and consolidation decision happens automatically without human supervision, so without metrics you will never know why a node was created, why it was removed, or why a pod is stuck as Pending.
This episode is about observability. You will learn to read the Prometheus metrics Karpenter exposes, understand what each metric group means, use a ready-made Grafana dashboard, and build alerting for two critical scenarios: pods that stay Pending too long and consolidation that fails or never happens.
A node autoscaler manages the most expensive resource in a cluster: EC2 machines. Unlike an HPA, which only changes replica counts, Karpenter creates and deletes real infrastructure, so an error here has a direct financial impact. Observability is not merely a want — it is a necessity to answer these questions:
| Metric Group | Question It Answers |
|---|---|
| Node | How much capacity is being provisioned for the cluster |
| Pod | How quickly pods are scheduled and become ready |
| Provisioning | How long scheduling and node creation take |
| Consolidation | Whether savings are happening without disrupting pods |
| Interruption | Whether Spot events and health checks are handled |
Metrics prefixed with karpenter_nodes_* describe the lifecycle of nodes managed by Karpenter. The most commonly used is karpenter_nodes_allocatable — a gauge holding the amount of resources available on nodes owned by Karpenter, with a resource label such as cpu, memory, and pods.
# Kapasitas CPU total yang disediakan Karpenter
sum(karpenter_nodes_allocatable{resource="cpu"})
# Jumlah node yang dibuat dan dihapus dalam satu jam terakhir
sum(increase(karpenter_nodes_created[1h]))
sum(increase(karpenter_nodes_terminated[1h]))
# Perbandingan resource yang terpakai vs total allocatable
sum(karpenter_nodes_allocatable{resource="cpu"}) -
sum(karpenter_node_utilization{resource="cpu"})Notice the karpenter_nodes_terminated metric together with the termination reason label. A spike here alongside a drop in pods can indicate consolidation is working, but if it keeps happening without a pause, there may be a problem with PDBs or disruption.budgets that are too aggressive.
karpenter_pods_startup_time_seconds is a histogram measuring the time from pod submission until the pod is ready to receive traffic. This is the best metric for evaluating Karpenter's core promise: provisioning latency in seconds. Dividing _sum by _count gives the average startup time.
# Rata-rata startup pod dalam detik
rate(karpenter_pods_startup_time_seconds_sum[5m]) /
rate(karpenter_pods_startup_time_seconds_count[5m])There is also karpenter_pods_pending_duration_seconds, which measures how long pods wait in the scheduling queue. If this metric is high while nodes are not being created, the problem is not provisioning speed but a NodePool constraint that is too narrow or exhausted AWS quotas.
Karpenter exposes karpenter_provisioner_scheduling_duration_seconds to measure the duration of scheduling decisions — from the moment a pod enters the queue until an instance type is selected. This metric matters because it separates two different sources of delay: Karpenter's internal compute time versus the AWS API time needed to launch an instance.
Tip
When evaluating Karpenter performance, keep the three durations separate: scheduling (provisioner metric), instance creation (cloudprovider metric), and pod startup (pod histogram). Treating them as one will lead you to the wrong conclusions while chasing a sub-30 second latency target.
Karpenter exposes the /metrics endpoint on port 8080 in the karpenter namespace. Prometheus can collect them through a ServiceMonitor, or with a direct scrape config like the example below:
scrape_configs:
- job_name: karpenter
kubernetes_sd_configs:
- role: endpoints
namespaces:
names:
- karpenter
relabel_configs:
- source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name]
regex: karpenter
action: keep
- source_labels: [__meta_kubernetes_endpoint_port_name]
regex: http
action: keepOnce scraping is active, verify that metrics are coming in via kubectl port-forward -n karpenter service/karpenter 8080:8080 and visit the endpoint. For production scale, store data longer with remote write or Thanos, because Karpenter metrics are most useful when they can be compared across weeks to spot cost and latency trends.
Building a dashboard from scratch takes time. Fortunately, the community and AWS provide ready-to-import dashboards. Karpenter ships the official JSON dashboard in its repository, and the aws-samples EKS deployment examples also include Karpenter panels alongside other node metrics.
karpenter_nodes_allocatable compared against karpenter_pods_startup_time_seconds is often enough to monitor efficiency.Important
A dashboard is only a visual aid. What matters more is consistent labeling. Make sure all Karpenter nodes are tagged according to their NodePool, for example with the karpenter.sh/discovery tag and a cost center label, so per-workload cost panels can be grouped correctly.
A pod sitting idle as Pending is the earliest signal that autoscaling has failed. Combining Kubelet and kube-state-metrics metrics produces an accurate alert:
groups:
- name: karpenter-alerts
rules:
- alert: KarpenterPodPendingLong
expr: |
time() - kube_pod_created > 300
and on(namespace, pod) kube_pod_status_phase{phase="Pending"} == 1
for: 5m
labels:
severity: warning
annotations:
summary: "Pod pending lebih dari 5 menit"When this alert fires, follow this diagnosis path: check NodeClaim status with kubectl get nodeclaims, inspect the controller logs with kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter, and make sure the EC2 instance limit has not been reached. Episode 18 covers this troubleshooting in depth.
Consolidation is a cost-saving feature, but it can also become a source of disruption when misconfigured. Monitor karpenter_consolidation_seconds_since_last to detect consolidation that has not run at all, and karpenter_consolidation_pods_evicted_total to see how many pods have been evicted.
# Berapa detik terakhir sejak konsolidasi terakhir berjalan
karpenter_consolidation_seconds_since_last
# Pod yang di-evict oleh konsolidasi dalam satu jam
sum(increase(karpenter_consolidation_pods_evicted_total[1h]))Warning
Watch out for repeated eviction patterns on the same pod. If the same pod keeps getting evicted by consolidation but never moves to a cheaper node, there is likely a problem with PDBs or unrealistic resource requests. Don't create an alert that only counts eviction volume without looking at the context of repetition.
Observability turns Karpenter from a black box into a system that is predictable and accountable.
Key takeaways:
karpenter_pods_startup_time_seconds and the scheduling metrics show the real latency users experience, not just cluster status.The metrics are ready to be monitored, but there is one aspect that determines whether your cluster is safe for production: networking. In episode 13 we discuss Networking Integration — how Karpenter selects subnets and Availability Zones, the role of security groups, how ENI and Pod ENI work, VPC CNI, and even EFA for HPC and ML workloads. See you there!