Skip to content
Documentation Background

Scaling & HPA

Kubernetes provides a layered approach to scaling: you can scale manually when you know your capacity needs, or let autoscalers respond to real-time demand. The two dimensions of scaling — Pods and nodes — can be combined for fully elastic infrastructure.


You can adjust the number of running Pods at any time using two methods. Unlike rolling updates, manual scaling is near-instantaneous.

Terminal window
kubectl scale deployment <name> --replicas=5

Edit spec.replicas in the manifest and re-apply:

spec:
replicas: 5 # updated from 3
Terminal window
kubectl apply -f deployment.yaml

This keeps your source YAML as the single source of truth, ensuring the live cluster never drifts from your declared state.

Terminal window
kubectl scale deployment <name> --replicas=0

All Pods are terminated but the Deployment and ReplicaSet objects remain. Scale back up at any time — useful for temporarily suspending a workload without losing its configuration.


For dynamic workloads, manually adjusting replicas is impractical. Kubernetes provides three autoscalers that react to real-time signals:

AutoscalerWhat it scalesDefault installedDisruption
HPANumber of Pods✅ YesNone — adds/removes Pods smoothly
Cluster Autoscaler (CA)Number of nodes✅ Yes (cloud)None for running Pods
VPACPU/memory per Pod❌ NoDisruptive — deletes and recreates Pods

The HPA automatically adds or removes Pods in a Deployment (or StatefulSet/ReplicaSet) based on observed metrics. It is the primary autoscaler for handling traffic fluctuations.

HPA

The HPA controller polls metrics at a configurable interval and computes the target replica count:

desired replicas = ceil(current replicas × (current metric / target metric))
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # scale out when average CPU > 70%
Terminal window
kubectl apply -f hpa.yaml
# Check HPA status and current metrics
kubectl get hpa
kubectl describe hpa my-app-hpa

Before an HPA can function, three conditions must be met:

RequirementDetail
Metrics ServerMust be installed and running. Without it, the HPA cannot evaluate workload performance. Allow a few minutes after installation for metric collection to begin.
Resource requests definedContainers must declare resources.requests.cpu (for CPU scaling) and/or resources.requests.memory (for memory scaling). These serve as the utilisation baseline for percentage calculations.
Sufficient cluster capacityThe cluster must have enough CPU and memory headroom to schedule additional Pod replicas when scaling out.
Metric typeWhat it measuresExample use case
Resource (CPU/memory)Average utilisation across PodsWeb servers, APIs
PodsCustom per-pod metricRequests per second
ObjectMetric from another K8s objectQueue depth on a Service
ExternalMetric from outside the clusterMessage queue depth (SQS, Pub/Sub)

You can evaluate both CPU and memory simultaneously — the HPA scales out if either threshold is exceeded:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-cache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app-cache
minReplicas: 3
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
- type: Resource
resource:
name: memory
target:
type: AverageValue
averageValue: 500Mi

The target Deployment’s Pod template must define both CPU and memory requests for multi-metric evaluation:

resources:
requests:
cpu: 250m
memory: 100Mi
limits:
cpu: 500m
memory: 500Mi
Terminal window
# Create an HPA targeting 80% CPU, scaling between 3–5 replicas
kubectl autoscale deployment <name> --cpu-percent=80 --min=3 --max=5
# Check current state
kubectl get hpa
Terminal window
# List all HPAs and their current metric values vs. targets
kubectl get hpa
# Detailed configuration, status conditions, and scaling events
kubectl describe hpa <name>

Sample kubectl get hpa output:

NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
app-cache Deployment/app-cache 1994752/500Mi, 0%/80% 3 5 3 2m14s

Status conditions in kubectl describe hpa:

ConditionWhat it signals
AbleToScaleHPA controller is ready to execute scaling operations
ScalingActiveHPA is successfully calculating replica counts from valid metrics
ScalingLimitedScaling is constrained — replicas have hit the min or max boundary

Troubleshooting <unknown> in the TARGETS column:

CauseFix
Pod template missing resources.requestsAdd CPU and/or memory requests to the container spec
Metrics Server not installed or unhealthyInstall/restart Metrics Server; allow a few minutes for metric collection

The Cluster Autoscaler operates at the infrastructure level — it adds or removes nodes rather than Pods. It works in tandem with the HPA.

When the HPA requests more Pods than the current nodes can accommodate:

  1. HPA instructs the scheduler to add Pods
  2. Scheduler cannot place Pods — marks them as Pending
  3. CA detects Pending Pods and provisions a new node from the cloud provider
  4. Once the node joins the cluster, the scheduler assigns the Pending Pods to it

When demand drops:

  1. HPA scales down Pods
  2. Nodes become underutilised
  3. CA safely evicts any remaining Pods from the underutilised node, rescheduling them elsewhere
  4. CA terminates the now-empty node

The VPA adjusts the CPU and memory requests/limits on existing Pods rather than changing their count. It is less common in production for these reasons:

  • Not installed by default — requires manual installation
  • Disruptive — currently scales by deleting the existing Pod and replacing it with one that has updated resource settings. In-place resource updates are under active development upstream
  • Conflicts with HPA — running both on CPU/memory metrics simultaneously is not recommended without careful configuration
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: Auto # Off | Initial | Recreate | Auto
Update modeBehaviour
OffRecommendations only — no automatic changes
InitialApply recommendations only at Pod creation
RecreateEvict and recreate Pods when adjustments are needed
AutoCurrently equivalent to Recreate (in-place update is not yet stable)

Combining the HPA and CA gives you fully elastic infrastructure — Pods scale horizontally with demand, and nodes scale to accommodate the Pods.

Traffic spike → HPA adds Pods → Nodes fill up → CA adds nodes → Pods schedule
Traffic drops → HPA removes Pods → Nodes underutilised → CA removes nodes

This is sometimes called multi-dimensional autoscaling and is the standard pattern for production cloud-native applications.


For workloads driven by external event sources (message queues, databases, HTTP request rate), the Kubernetes Event-Driven Autoscaler (KEDA) extends the HPA with custom scalers. It can scale from zero (no Pods when idle) to many, based on signals like:

  • Queue depth (RabbitMQ, Kafka, SQS, Azure Service Bus)
  • Cron schedule
  • Prometheus query result
  • HTTP request rate

KEDA is a CNCF project and a common complement to the built-in HPA in event-driven architectures. For multi-cluster scaling at enterprise scale, community projects like Karmada extend these capabilities across multiple Kubernetes clusters.