Designing an optimal production NodePool: multi-AZ workload distribution, mixing Spot and On-Demand, alignment with Reserved Instances and Savings Plans. Plus FinOps practices for per-workload cost allocation based on tags and a cost comparison before and after Karpenter.

In episode 14 you secured Karpenter with minimal IAM and NodePool isolation. But an infrastructure that is both secure and smooth can still be wasteful. In the cloud, Karpenter's provisioning speed actually opens new avenues for waste: nodes created too fast, terminated too slowly, or expensive instance families used for workloads that could sit on far cheaper instances.
This episode combines two things: capacity best practices and FinOps. You will learn to design a production NodePool with multi-AZ distribution, take advantage of a Spot and On-Demand mix, align Reserved Instance and Savings Plans purchases, and then measure Karpenter's financial impact with tag-based cost allocation and a before-after comparison.
Don't concentrate workloads in a single Availability Zone. A single AZ creates two problems: an availability risk when the AZ is disrupted, and capacity that often runs out. Karpenter chooses AZs based on binpacking and cost, but you must make sure all the AZs you want are available as candidates.
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: general
spec:
template:
spec:
requirements:
- key: topology.kubernetes.io/zone
operator: In
values:
- ap-southeast-1a
- ap-southeast-1b
- ap-southeast-1c
disruption:
consolidationPolicy: WhenUnderutilizedNote that the ap-southeast-1c zone may have different capacity than the others. Let Karpenter decide the distribution based on actual availability, but still provide three options so a single zone failure does not stop provisioning.
Spot can save 60-90 percent off the on-demand price, but instances can be reclaimed with a short notice. A common strategy: stateless, interruptible workloads sit on Spot, while stateful and critical workloads sit on On-Demand. Karpenter models this through capacity types in the NodePool.
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: hybrid
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: 720hIf the capacity values in the NodePool include both spot and on-demand, Karpenter still prioritizes Spot when available and automatically falls back to on-demand when Spot capacity is exhausted. Workloads that must not be interrupted can be separated into an on-demand-only NodePool, with taints and tolerations as taught in episode 14.
| Scenario | Strategy |
|---|---|
| Batch and jobs | Full Spot |
| APIs and critical services | On-Demand, with a spare node buffer |
| Mixed | One spot and on-demand NodePool |
| Dedicated tenancy | Separate NodePool with a taint |
Discounts from Reserved Instances and Savings Plans only apply if the instances you bought are actually used. Karpenter does not automatically know about your commitment purchases, so there is a risk: you bought instances for the m5.large type, but Karpenter picks m5.xlarge and the discount goes untouched.
A practical approach to alignment:
node.kubernetes.io/instance-family.spec:
template:
spec:
requirements:
- key: node.kubernetes.io/instance-family
operator: In
values: ["m5", "m6i"]Important
Don't let purchase commitments dictate your entire architecture. If the only way to absorb the discount is to force every workload onto a specific instance family, reevaluate — the overcommitment cost of the wrong instance can exceed the discount you gain.
Tags are the key to FinOps on AWS. Every node Karpenter creates can be tagged automatically based on NodePool and workload, and the costs can then be grouped in Cost Explorer and the Cost and Usage Report. Make sure tagging is enabled from the start, because changing it after old nodes disappear means losing historical data.
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
amiFamily: AL2
tags:
Project: my-platform
Owner: platform-team
CostCenter: 12345Combine static tags in the NodeClass with dynamic tags: cost exporters such as Kubernetes Cost Allocation group node costs into the namespaces and workloads that use them. This way you can answer a simple question: which application is the most expensive to run.
Consolidation is Karpenter's savings engine, and it must be monitored to make sure it actually works. The karpenter_consolidation_pods_evicted_total and karpenter_consolidation_seconds_since_last metrics from episode 12 become the basis of periodic FinOps reports.
# Rata-rata jumlah node sebelum dan sesudah konsolidasi
avg(karpenter_nodes_allocatable{resource="cpu"}) /
sum(karpenter_nodes_allocatable{resource="cpu"})
# Lama waktu satu daur konsolidasi
histogram_quantile(0.95, rate(karpenter_consolidation_duration_seconds_bucket[5m]))To validate Karpenter, compare costs before and after the migration over equivalent periods. Do this with an honest methodology: periods of equal length, comparable workloads, and no public price changes in between.
| Metric | Before Karpenter | After Karpenter |
|---|---|---|
| Node cost per month | Baseline | Result after consolidation |
| Average active nodes | Measured manually | Measured from Karpenter metrics |
| Average CPU utilization | Low | Target 60-80 percent |
| Node provisioning time | Minutes | Seconds |
Warning
Don't compare different months directly without normalization. Traffic can double during a promo season, or Spot prices can shift drastically. Compare the relative cost per unit of workload, for example cost per request or per active pod, rather than absolute monthly figures.
Optimal capacity and controlled costs are two sides of the same coin when operating Karpenter.
Key takeaways:
Best practices and FinOps make Karpenter deliver measurable savings. But many teams are still running the old Cluster Autoscaler. In episode 16 we cover Migration from Cluster Autoscaler — when to switch, a gradual coexistence strategy, converting nodegroups into NodePools, behavioral differences, and downtime management. See you there!