Skip to main content

Static Capacity Pools

What are static capacity pools?

Setting spec.replicas on a NodePool tells EKS Auto Mode to maintain exactly N nodes at all times, regardless of pod demand. These nodes are never consolidated away when empty. They exist whether workloads are scheduled on them or not.

Prerequisites

Cluster deployed and kubectl configured per Quick Start.

spec:
replicas: 2 # Always maintain exactly 2 nodes

Why they exist

Some workloads need guaranteed capacity available at all times:

  • Always-on inference endpoints that must respond in milliseconds, not minutes
  • License-bound software where the license is tied to a running host
  • Latency-sensitive services where waiting for a node to provision (30-90s) is unacceptable

Dynamic scaling introduces cold-start latency. For workloads where that latency violates SLOs, static pools eliminate it entirely.

How it differs from dynamic pools

BehaviorDynamic PoolStatic Pool
Scale-from-zeroYesNo -- always N nodes
ConsolidationRemoves underutilized nodesNever removes nodes
Scaling triggerPending podsManual (edit spec.replicas)
Cost modelPay only for what you usePay for N nodes 24/7
Cold-start riskYes (30-90s node provision)None

You can resize a static pool at any time:

kubectl patch nodepool static-gpu-nodepool --type=merge -p '{"spec":{"replicas":4}}'

The trade-off

You pay for N nodes 24/7 whether they are fully loaded or completely idle. Use static pools only where:

Cost of idle nodes < Cost of cold-start latency

If your workload can tolerate 60-90 seconds of scale-up time, a dynamic pool is cheaper.

When to use

  • Always-on model serving (vLLM, TGI, Triton)
  • License servers (FlexLM, RLM)
  • Baseline capacity layer -- handle steady-state traffic with static nodes, burst traffic with a separate dynamic pool

Deploy

Cost warning: This example provisions GPU instances (g6e family) which are billed per-second while running. Nodes will launch immediately upon apply and persist until you clean up. Estimated cost: ~$1.50/hr per node.

kubectl apply -f static-nodepool.yaml

What to observe

Verify exactly 2 nodes exist for this pool (even with zero pods):

kubectl get nodes -l karpenter.sh/nodepool=static-gpu-nodepool

Confirm replicas are set:

kubectl get nodepool static-gpu-nodepool -o jsonpath='{.spec.replicas}'

Watch that consolidation does NOT touch these nodes:

kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter -f | grep static-gpu

Clean up

kubectl delete -f .