Karpenter v1.15.0 in practice: VPA prediction, disruption and provisioning
A platform can launch nodes successfully and still deliver the wrong capacity. Undersized requests, storage constraints and differences between scheduling simulations and the real scheduler can leave pods pending or create unnecessary workload movement. Karpenter v1.15.0, released on October 10, 2026, includes changes in these areas and expands disruption instrumentation. The AWS provider also has a v1.15.0 release. (github.com)
The most important qualification: VPA prediction is not operationally integrated in this release. An implementation of the prediction controller and store exists, but controller registration was removed before release while prediction integration remained unfinished. Upgrading to v1.15.0 does not enable VPA-driven predictive provisioning. (github.com)
This guide distinguishes delivered behavior from internal groundwork and proposes a production validation workflow.
1. Components and ownership boundaries
Three objects organize capacity in Karpenter:
- NodePool: defines requirements and policies for a set of nodes.
- NodeClaim: represents an individual capacity request and tracks its lifecycle.
- EC2NodeClass: holds AWS-specific settings, including images, subnets, security groups and node identity. It complements rather than replaces the NodePool. (karpenter.sh)
The Vertical Pod Autoscaler — VPA recommends container resources and can apply them according to its update policy. Karpenter does not install it automatically. VPA configuration uses a separate VerticalPodAutoscaler Custom Resource Definition — CRD. (raw.githubusercontent.com)
The simplified provisioning path is:
Unschedulable pod → Karpenter simulation → matching NodePool → NodeClaim → provider → registered node → kube-scheduler places the pod
Resource requests, placement requirements and overhead influence this process. Karpenter supplies capacity; kube-scheduler remains responsible for binding pods to nodes. (karpenter.sh)
Requests are not measurements of actual consumption. They guide scheduling, whereas limits constrain runtime resource use. CPU limits can introduce throttling, and exceeding a memory limit can result in an out-of-memory termination. (kubernetes.io)
2. VPA prediction: implementation is not activation
Change #3134 implemented the write path: reading VPA recommendations and storing predicted resource requests after resizing. The intended use was to account for upcoming resizes during consolidation. The pull request explicitly separated this work from the read path that would consume predictions in disruption decisions. (github.com)
On October 8, 2026, change #3383 removed controller registration until that integration was ready. In the v1.15.0 source tag, NewControllers does not register the VPA prediction controller. (github.com)
The operational implications are straightforward:
- This guide provides no supported procedure to activate VPA prediction in Karpenter.
- Do not add invented feature gates or NodePool fields.
- A value in
status.recommendationis not an applied resource request. - VPA can still support right sizing independently; that does not demonstrate predictive integration with Karpenter. (raw.githubusercontent.com)
A hypothetical capacity example
Consider 12 workers, each requesting 512Mi of memory. A recommendation of 1Gi per worker would add 6Gi of aggregate requests if applied to every replica. This arithmetic excludes overhead, fragmentation, zone placement and volume constraints.
Treat the recommendation as capacity-planning input, not as an advance reservation created by Karpenter v1.15.0. My operational recommendation is to load-test the new size and roll it out gradually while monitoring available capacity and recovery time.
3. Changes that actually affect provisioning
Volume limits before instance launch
Change #3202 allows providers to declare expected per-driver Container Storage Interface — CSI — volume limits on instance types. Scheduling simulation can consider those limits before a CSINode exists. Once the node registers, its CSINode information takes precedence. Providers that do not populate the limits do not automatically gain this behavior. (github.com)
For validation, I suggest launching a batch of pods with PersistentVolumeClaims — PVCs — representative of the application. Compare how many remain pending after the first provisioning round rather than assuming CPU and memory are the only capacity constraints.
Scheduler default topology constraints
Change #3205 adds --scheduler-config, also exposed through SCHEDULER_CONFIG, to supply podTopologySpread.defaultConstraints to the simulation. It does not automatically discover kube-scheduler configuration. An empty configuration leaves behavior unchanged. (github.com)
My preference is to declare important placement constraints on workloads and test them against the available NodePools. On Amazon EKS, do not assume unrestricted access to managed scheduler configuration.
Capacity failures become visible on pods
An instance launch failure caused by insufficient capacity now produces an InsufficientCapacityError event on pods nominated for the affected NodeClaim. Application operators can investigate closer to the workload instead of depending exclusively on controller logs. (github.com)
4. Practical configuration with explicit prerequisites
The following examples assume:
- compatible Karpenter controllers and CRDs are installed;
- a validated
EC2NodeClassnamedproductionalready exists; - the
productionnamespace andorders-workerDeployment already exist; - VPA is installed separately with a working recommender and metrics source;
- worker pods select
karpenter.sh/nodepool: apps-ondemand.
The values are illustrative and require capacity and availability review before production use.
A conservative NodePool for initial testing
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: apps-ondemand
spec:
template:
metadata:
labels:
workload-tier: application
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: production
requirements:
- key: kubernetes.io/os
operator: In
values: [linux]
- key: kubernetes.io/arch
operator: In
values: [amd64]
- key: karpenter.sh/capacity-type
operator: In
values: [on-demand]
expireAfter: 720h
limits:
cpu: "200"
memory: 800Gi
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 5m
budgets:
- nodes: "1"This NodePool constrains architecture and purchasing model, references existing AWS configuration and limits provisioned resources. consolidateAfter introduces a delay after pod changes; it is not a global minimum interval between all disruptions. NodePool limits are not a hard spending cap and can be temporarily exceeded during concurrent provisioning. (karpenter.sh)
The one-node budget restricts budget-controlled disruptions. It does not prevent external interruptions or make expiration risk-free. Treat expireAfter: 720h as a maintenance policy. Omitting terminationGracePeriod also requires a plan for blocked drains. (karpenter.sh)
For the initial production experiment, I suggest On-Demand capacity to isolate variables. Evaluate Spot later for suitable workloads and pools; this example is not a universal purchasing policy.
Recommendation-only VPA
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: orders-worker
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: orders-worker
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: worker
controlledResources: [cpu, memory]
minAllowed:
cpu: 250m
memory: 256Mi
maxAllowed:
cpu: "2"
memory: 2GiOff preserves recommendations without changing pod resources. targetRef must identify the actual Deployment, and containerName must match its container. Policy bounds constrain recommendations; they do not replace resources.limits. This configuration does not activate Karpenter prediction. (raw.githubusercontent.com)
Protecting the workload during evictions
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: orders-worker
namespace: production
spec:
maxUnavailable: 1
selector:
matchLabels:
app: orders-workerA PodDisruptionBudget — PDB — constrains unavailability considered by the eviction API for matching pods. For this scenario, I suggest at least three healthy replicas with consistent labels. A PDB does not guarantee survival of a zone failure and does not replace the Deployment rollout strategy. (kubernetes.io)
Configure readiness, liveness and, where appropriate, startup probes around application behavior. Readiness indicates whether a pod can receive traffic. Liveness should not restart workers merely because an external dependency is temporarily unavailable. (kubernetes.io)
5. Disruption metrics: decisions are not downtime
The changelog contains a naming trap. The original change title mentions karpenter_pods_disrupted_total, but the delivered code uses `karpenter_pods_disruption_initiated_total`. It adds the number of reschedulable pods when NodeClaim disruption begins, excluding DaemonSet and mirror pods. karpenter_nodeclaims_disrupted_total counts NodeClaims rather than pods. (github.com)
The new consolidation_policy and termination_mode dimensions distinguish policy and termination context. Termination mode derives from terminationGracePeriod: absent means graceful, positive means eventual, and zero means forceful. An eventual series does not prove that a particular pod was forcibly deleted. (github.com)
To track initiated pod disruptions by pool and reason:
sum by (nodepool, reason) (
increase(karpenter_pods_disruption_initiated_total[1h])
)To compare operational contexts:
sum by (nodepool, consolidation_policy, termination_mode) (
increase(karpenter_nodeclaims_disrupted_total[1h])
)These queries reference counters present in the v1.15.0 tag. Add cluster scoping using your collection labels. Do not add decision, initiation and drain metrics together as though they represented separate impact events. (raw.githubusercontent.com)
increase accounts for counter resets and extrapolates across the selected window, so fractional results are possible. A missing series is not necessarily zero; verify collection before automatically filling gaps. (prometheus.io)
Additional signals include karpenter_pods_drained_total, karpenter_pods_eviction_requests_total and karpenter_voluntary_disruption_queue_failures_total. The pod metrics discussed here are alpha, so review their contracts during upgrades. (karpenter.sh)
I suggest a dashboard with four perspectives: created capacity, workload movement, pending pods and application health. Correlate events with latency, errors, throughput, restarts and queue depth. Alert primarily on sustained service-level objective — SLO — degradation rather than on every consolidation action.
6. An enterprise scenario: workers under variable demand
Problem: a team observes rising backlog and frequent worker movement during campaigns.
Proposed solution: separate resource investigation, request changes and consolidation policy changes.
Implementation:
- Establish a baseline for backlog, consumption, requests, latency and disruption.
- Run VPA in
Offmode over a representative workload window. - Review recommendations alongside concurrency settings and connection pooling.
- Apply revised requests to a controlled worker cohort.
- Compare behavior before changing budgets or purchasing models.
Expected outcome, not a benchmark: identify whether the constraint is CPU, memory, database capacity, queue processing or node availability while preserving change traceability. No savings percentage or incident reduction is assumed.
7. Production reliability, security and FinOps
My proposed architecture keeps critical controllers on stable capacity and application workloads on elastic capacity. For Spot, configure interruption handling and retain compatible instance-type and zone diversity. AWS recommends avoiding a design in which Karpenter depends entirely on the capacity it provisions to run its own controller. (docs.aws.amazon.com)
For high availability, spread replicas across nodes and zones. Hard placement constraints can preserve isolation but also prevent scheduling when capacity is unavailable. Validate that trade-off through failure testing. (kubernetes.io)
My security checklist includes:
- least-privilege role-based access control — RBAC — for operators and automation;
- AWS workload identities, such as IAM Roles for Service Accounts — IRSA — or EKS Pod Identity, according to the validated installation;
- no permanent credentials in manifests;
- private metrics endpoints, NetworkPolicies supported by the network plugin and TLS where required;
- verified images, protected Secrets and documented credential rotation. (kubernetes.io)
For FinOps, evaluate cost per completed unit of work rather than node count alone. Include storage, cross-zone networking, load balancers and observability retention in the review. I recommend accepting an optimization only when SLO behavior remains stable and recovery tests meet expectations.
A development environment may use one replica and less redundancy to simplify experimentation. Production should have explicit acceptance criteria for replicas, probes, resources, alerts and rollback. For persistent data, include backups and restoration exercises; autoscaling should not be treated as a substitute for data recovery.
8. Upgrade and rollback planning
Version v1.15.0 also moves Node Auto Repair, when enabled, into the voluntary disruption path. The API adds Unhealthy as a budget reason. Upgrade CRDs with the controller; do not assume that upgrading the main Helm chart automatically updates CRDs installed earlier. (karpenter.sh)
Before the change, I suggest recording configuration and version information:
kubectl -n kube-system get deployment karpenter -o yaml
kubectl get nodepools -o yaml
kubectl get ec2nodeclasses -o yaml
kubectl get nodeclaims
helm list -A
helm show chart oci://public.ecr.aws/karpenter/karpenter --version 1.15.0Adjust the namespace and installation name. Retain outputs through the change-management process without publishing sensitive configuration.
Review dashboards as part of the upgrade. reason values have been standardized to lowercase. On karpenter_voluntary_disruption_queue_failures_total, consolidation_type now represents types such as empty, single and multi, rather than decisions such as delete. (github.com)
My staging checklist includes provisioning new capacity, replacing a node, testing a blocked eviction and validating alert behavior. Keep previous charts, values and manifests available for rollback, and verify CRD compatibility. Deleting CRDs or removing finalizers should not be the normal reversal procedure.
9. Troubleshooting
VPA recommends more memory, but resources stay unchanged
Symptom → recommendations appear while requests remain unchanged.
Possible cause → updateMode: Off, which is intentional.
Investigation → compare configuration and status:
kubectl -n production get vpa orders-worker -o yaml
kubectl -n production get deployment orders-worker -o yamlCorrection → apply reviewed requests through the delivery process or evaluate an update mode supported by your VPA version. Do not try to activate the missing predictive integration in Karpenter v1.15.0. (raw.githubusercontent.com)
Pods remain pending because capacity is unavailable
Symptom → pods do not start.
Possible cause → unavailable capacity for the permitted instance-type and zone combination.
Investigation → inspect events, NodeClaims and logs:
kubectl get pods -A
kubectl get nodes
kubectl get nodeclaims
kubectl -n production describe pod <pod>
kubectl get events -A --sort-by=.metadata.creationTimestamp
kubectl -n kube-system logs deployment/karpenter --since=30mCorrection → when the event confirms InsufficientCapacityError, expand compatible capacity options. Do not remove mandatory architecture or security requirements merely to make scheduling succeed. (github.com)
A node does not consolidate
Symptom → apparently idle capacity remains.
Possible cause → a PDB, disruption budget, disruption protection or placement constraint.
Investigation → inspect conditions and events:
kubectl get pdb -A
kubectl describe nodepool apps-ondemand
kubectl describe nodeclaim <nodeclaim>Correction → adjust the blocking constraint with workload-owner approval rather than removing protections indiscriminately. (karpenter.sh)
10. When to use it and what to consider instead
I recommend Karpenter when capacity flexibility justifies the operational investment in policies, observability and disruption testing. A small team with stable demand may benefit more from a simpler design.
On Amazon EKS, managed node groups with Cluster Autoscaler provide a group-based capacity alternative. Karpenter offers a different selection and provisioning model; a new release alone is not a reason to migrate. VPA complements node scaling rather than replacing it. (docs.aws.amazon.com)
Practical summary
- Do not mistake VPA prediction code for an active v1.15.0 integration.
- Validate requests before making consolidation more aggressive.
- Measure application impact, not just controller activity.
- Test storage constraints, topology and capacity failures.
- Update observability, CRDs and rollback planning alongside the controller.
Conclusion
For DevOps, SRE and Platform Engineering teams, the important decision is not simply whether to install a new version. It is determining which behaviors are available, which signals demonstrate correct operation and which safeguards protect the application.
The proposed approach is incremental: recommendations first, controlled resource changes next, and optimization only when supported by operational evidence. That makes autoscaling a governed platform capability rather than an automation whose consequences become visible only during an incident.
Related articles
Want to apply this to your environment?
Book a free 30-minute call. I will look at your setup and tell you where to start.
Book a callGet new articles by email
One email when a new article on infrastructure, backend and AI is published. No spam, unsubscribe anytime.