Kubernetes autoscaling has three independent knobs: the HorizontalPodAutoscaler (HPA) changes replica count, the VerticalPodAutoscaler (VPA) changes per-Pod CPU/memory requests, and the Cluster Autoscaler changes the number of Nodes. All three read from the same Metrics Server.
Architecture Overview

Prerequisites
kubectl version --client
kubectl get deployment metrics-server -n kube-system
Step 1: Install the Metrics Server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl get deployment metrics-server -n kube-system
kubectl top nodes
kubectl top pods
Step 2: Deploy a Workload with Resource Requests
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: registry.example.com/web:2.1.0
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
kubectl apply -f deployment.yaml
kubectl get deployment web
Step 3: Create a HorizontalPodAutoscaler
kubectl autoscale deployment web \
--cpu-percent=60 \
--min=3 \
--max=10
kubectl get hpa web
kubectl describe hpa web
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
Step 4: Load Test and Watch It Scale
# In one terminal, watch the HPA react
kubectl get hpa web --watch
# In another terminal, generate load
kubectl run load-generator --image=busybox --restart=Never -- \
/bin/sh -c "while true; do wget -q -O- http://web; done"
kubectl get pods -l app=web
kubectl delete pod load-generator
Step 5: Right-Size Requests with a VerticalPodAutoscaler
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: web
updatePolicy:
updateMode: "Auto"
kubectl apply -f vpa.yaml
kubectl describe vpa web-vpa
# recommendation section shows suggested CPU/memory requests
Step 6: Scale Nodes with the Cluster Autoscaler
kubectl -n kube-system get deployment cluster-autoscaler
kubectl -n kube-system logs deployment/cluster-autoscaler \
--tail=50 | grep -i "scale"
# Pods that cannot be scheduled trigger a scale-up
kubectl get pods --field-selector=status.phase=Pending
kubectl describe pod <pending-pod> | grep -A5 Events
Autoscaling Cheat Sheet
kubectl top nodes # node CPU/memory usage
kubectl top pods # pod CPU/memory usage
kubectl get hpa # list HorizontalPodAutoscalers
kubectl describe hpa <name> # see current/target metrics
kubectl get vpa # list VerticalPodAutoscalers
kubectl autoscale deployment <name> \
--cpu-percent=60 --min=2 --max=10 # quick HPA from the CLI
kubectl get pods --field-selector=status.phase=Pending # find scale-up triggers
FAQ
Can I run HPA and VPA on the same Deployment? Not safely for CPU/memory at the same time; they can fight over the same signal. A common pattern is HPA on custom or memory metrics while VPA manages CPU/memory requests, or VPA in recommendation-only mode.
Does HPA work without the Metrics Server? No, the default CPU/memory-based HPA needs the metrics.k8s.io API that the Metrics Server provides. Custom or external metrics need a separate adapter.
What triggers the Cluster Autoscaler? Pods stuck in Pending because no Node has enough allocatable CPU or memory. It then adds Nodes from a configured node group, and removes underutilized Nodes later.
Summary
You installed the Metrics Server, created an HPA that scales replicas on CPU utilization, added a VPA to right-size resource requests, and confirmed how the Cluster Autoscaler reacts to Pending Pods by adding Nodes, all through kubectl.
