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 .