Outcome
By the end of this lesson, you will be able to read HPA signals to identify right-sizing opportunities, diagnose the four common HPA patterns (at-min, at-max, oscillating, balanced), and tune HPA settings based on observed behavior.
| Tier | Engineer |
| JTBD | ”Read what HPA is telling me: am I over-provisioned, under-provisioned, or just right?” |
| Personas | Platform Engineer · SRE · DevOps Engineer |
| Prerequisites | M5.3.L1 (requests/limits) |
| Time | 9 minutes |
| Bloom verb | Read (Understand), Diagnose (Analyze), Tune (Apply) |
1. Concept
The Horizontal Pod Autoscaler adds and removes copies of a workload as load changes, watching processor use, memory or a metric of your own.
What it does is useful. What it remembers is more useful. Its history is a record of how much demand this workload has really seen, which is exactly the thing you would otherwise be guessing at when deciding how big it should be.
HPA STATUS shows: Current replicas (how many running now) Desired replicas (what HPA wants) Min / max settings (bounds you set) Current resource usage vs target ScalingLimited condition (if any: important!)
HPA HISTORY (over 30 days) shows: How often at min (over-provisioned at min if always there) How often at max (under-provisioned at max if always there) Oscillation pattern (bursty workload signal) ScalingLimited events (capacity constraint signal)The history is the goldmine. The current state is a snapshot; the history is a diagnosis.
The “ScalingLimited” condition: the strongest signal
ScalingLimited = HPA wants to scale but can't (typically hit max)
EXAMPLE: HPA target: 70% CPU Workload at: 95% CPU (clearly above target) HPA wants to scale OUT to handle the load But: pod count at max (e.g., 10) HPA stuck at 10 replicas; ScalingLimited fires
This is a strong signal: Workload is UNDER-provisioned at max Either increase max replicas Or right-size pods up (bigger pods = fewer needed)ScalingLimited events should be near zero. Any frequency >once/week = under-provisioning.
Four common HPA patterns
PATTERN 1: STUCK AT MIN HPA at minimum replicas for >50% of the time CPU below target most hours
DIAGNOSIS: Min too high WORKLOAD: Over-provisioned at the low end ACTION: Reduce min replicas COST IMPACT: 20-40% savings on low-traffic hours
PATTERN 2: STUCK AT MAX HPA at maximum replicas for >20% of the time ScalingLimited events frequent CPU at or above target
DIAGNOSIS: Max too low OR pods too small WORKLOAD: Under-provisioned at the high end ACTION: Increase max OR right-size pods up COST IMPACT: maybe higher cost; lower latency / better UX
PATTERN 3: OSCILLATING (rapid scale up/down) HPA replica count changing >5×/hour Constant scale up → scale down → scale up cycle
DIAGNOSIS: Bursty workload; HPA reacting to peaks WORKLOAD: Spiky but autoscaler can keep up ACTION: Increase scaling stabilization window OR set target higher (less sensitive) OR add KEDA / custom metrics for predictive scaling COST IMPACT: thrash creates churn; smoothing reduces wasted scaling
PATTERN 4: BALANCED (healthy) HPA at desired ≈ current always Replica count varies smoothly with load Rarely at min or max
DIAGNOSIS: Workload matches target well ACTION: No action needed; maintain current settings COST IMPACT: OptimalMost workloads in unmaintained clusters: 30% Pattern 1, 10% Pattern 2, 10% Pattern 3, 50% Pattern 4. Tuning the first three categories captures the savings.
Investigation pattern
ZopNight surfaces HPA signals automatically:
ZopNight HPA Recommendations: Filter: K8s workload type Sort by: HPA-derived recommendation impact
RECOMMENDATIONS surface as:
"Workload spent 18 hrs/day at min" → Consider reducing min replicas → Estimated savings: $X/mo
"Workload hit ScalingLimited 47 times in past week" → Consider increasing max OR right-sizing pods up → Risk: latency or pod evictions during spikes
"Workload oscillating between 4 and 10 replicas, 50+ events/day" → Consider scaling stabilization window → Or: predictive scaling via KEDAEach recommendation comes with the evidence (the HPA history) for the engineer to verify before applying.
HPA + scheduling interaction
WHEN HPA AND SCHEDULE BOTH ACTIVE: Schedule sets the replica RANGE (min, max) HPA scales WITHIN that range based on metrics
EXAMPLE: Schedule: BUSINESS HOURS: min=4, max=12 Schedule: OFF-HOURS : min=1, max=4 HPA: targets 70% CPU
During business hours: HPA scales 4-12 based on CPU During off-hours: HPA scales 1-4 based on CPU
COMBINED EFFECT: Schedule provides the floor (cost control) HPA provides the dynamic adjustment (responsiveness)
Together: low cost during off-hours, full capacity during peaksThe combo is the cost+performance sweet spot for most workloads.
HPA’s limitations
HPA SCALES POD COUNT, NOT POD SIZE If pods need more memory: HPA can't help Need to adjust resource requests/limits manually Or: VPA (Vertical Pod Autoscaler): separate tool
HPA SCALES BASED ON METRICS YOU PROVIDE CPU: standard, always available Memory: often needs custom metric setup Custom metrics (queue depth, request rate): need configuration Tools: Prometheus Adapter, KEDA
HPA HAS LATENCY Detection: ~30 seconds Scale-up: 30-60 seconds typically For sub-second spike response: HPA isn't fast enough Use over-provisioning + queue-based load shedding for ultra-low-latencyHPA is the workhorse for replica scaling, but it’s not a panacea. Pair with right-sizing (L1), schedules (M5.2), and custom metrics where needed.
Common HPA mistakes
MISTAKE FIX──────────────────────────────────────────────────────────────────Setting min replicas too high Measure off-peak utilization"Just in case" thinking Reduce min until off-peak is healthy (CPU 30-60%)
Setting max replicas too low Watch for ScalingLimitedLimits scale-out during real spikes events; increase maxUser impact during peaks OR right-size pods up
Wrong target value (too high) Target at 80% → scaling neare.g., 80% saturation; latency suffers Set 65-70% for most workloads
Wrong target value (too low) Target at 30% → constantlye.g., 30% over-provisioned Set higher; only go low for extremely latency-sensitive
Custom metric mis-configured Verify metric in Prometheus("queue depth" reports wrong) BEFORE relying on HPA action
HPA + autoscaler not coordinated Cluster autoscaler scales nodesHPA scales pods but no nodes Both must be configured See your cluster setupThe fixes are mostly observational + iterative. Set, measure, tune.
Tuning HPA: concrete process
QUARTERLY HPA TUNING (per workload):
WEEK 1: MEASURE Pull 30 days of HPA history Categorize: stuck-at-min / stuck-at-max / oscillating / balanced Quantify: % time at extremes, scaling event count
WEEK 2: DIAGNOSE For each non-balanced workload: Identify the issue (too high min, too low max, etc.) Estimate cost impact of fixing
WEEK 3: APPLY Pick top 3 highest-impact tunings Update HPA spec (kubectl apply) Monitor for 1 week
WEEK 4: VERIFY Confirm pattern moved toward balanced Confirm no degradation (latency, error rate, OOM) Document changesA workload running smoothly for 6 months still benefits from this review: traffic patterns change.
2. Demo
A real HPA tuning case:
WORKLOAD: order-processor (e-commerce, payment-team)HPA SETTINGS: min=4, max=12, target=70% CPU
OBSERVATIONS OVER 30 DAYS:
Replica count distribution: At 4 replicas (min): 62% of time (overnight + low-traffic) At 5-7 replicas: 28% of time (normal business hours) At 8-10 replicas: 9% of time (peak times) At 11-12 replicas: <1% of time ScalingLimited events: 0
CPU utilization: Daily avg: 38% Daily p95: 62% Daily p99: 78%
DIAGNOSIS: PATTERN 1: STUCK AT MIN (62% of time) Min too high: 4 replicas pinned during off-peak Each replica at ~25% CPU during off-peak Waste: 4 replicas × 75% unused capacity = 3 replica-equivalents wasted
Max also too high: never reached 12 Could reduce max to 10 without losing capacity
RECOMMENDATIONS: Reduce min from 4 to 2 Off-peak: 2 replicas handle the light load HPA scales up to 4-7 during business hours as needed Reduce max from 12 to 10 Never used 12; 10 is comfortable peak Keep target at 70% p95 CPU 62% within target; healthy
COST IMPACT: Each replica: ~$0.04/hr Off-peak window: ~14 hrs/day (62% of 24) Savings per day: 2 replicas saved × 14 hrs × $0.04 = $1.12 Monthly savings: $1.12 × 30 = $34/mo per workload
Doesn't sound huge BUT scales across: 18 workloads with similar pattern: ~$612/mo = $7,344/yr
DEPLOYMENT: Update HPA spec: minReplicas: 2 maxReplicas: 10 target: 70% (unchanged)
Monitor: 2 weeks Verify off-peak CPU stays healthy (<80%) Verify business hours scaling reaches 4-7 as expected Watch for any latency degradation
VERIFICATION (after 2 weeks): Off-peak CPU at 2 replicas: 65% (healthy) Business hours scaling: 4-6 replicas typical (matches old) No latency change Saved: $34/mo as projected
DECISION: roll out pattern to other 17 similar workloads (similar low-utilization-at-min pattern)The pattern compounds. Quarterly review across cluster: $5K-15K/year savings typical.
3. Hands-on (5 min)
Audit one of your HPA-managed workloads:
□ STEP 1: Pick a workload Workload: __________ Current min: ___ max: ___ target: ___%
□ STEP 2: Pull HPA history (30 days) $ kubectl describe hpa <workload> OR: ZopNight K8s detail view
Time at min: _____ % of period Time at max: _____ % of period ScalingLimited events: _____
□ STEP 3: Classify pattern □ Stuck at min (>50% time) □ Stuck at max (>20% time) □ Oscillating (>5 events/hr) □ Balanced
□ STEP 4: Plan tuning Action: __________ Estimated savings: $_____/mo
□ STEP 5: Apply + observe Apply: __________ Monitor for: ___ days Verification metric: __________A 15-minute audit per workload reveals patterns. Address top 3 each quarter.
4. Knowledge check
Q1
HPA “ScalingLimited” condition firing frequently:
A. Proof the HPA is scaling correctly
B. A metrics-server outage somewhere on the cluster itself now
C. Successful scaling
D. Signal that HPA wanted to scale but couldn’t (typically hit max)
Show answer
Correct: D. Strong indicator of under-provisioning at the high end. Real recommendation: increase max OR right-size pods up. Don’t ignore ScalingLimited: it correlates with user-visible latency. Indicates capacity constraint; latency-impacting.
Q2
A workload at min replicas for 62% of time (18 hrs/day):
A. Optimal: that’s autoscaling working
B. Likely over-provisioned at the min
C. Increase min
D. Evidence the HPA target is set too low
Show answer
Correct: B. Reduce min to lower the off-peak baseline. Save 20-40% of pod cost during off-peak. Verify off-peak CPU stays healthy after reduction. Reduce min; save off-peak cost.
Q3
HPA target value of 80% CPU:
A. Aggressive: scaling triggers near saturation
B. Conservative; safe
C. Too low: it scales the workload out far too early
D. Optimal
Show answer
Correct: A. Latency suffers because pods are already near their limit when HPA finally decides to scale. Set target 65-70% for most workloads (more headroom during scaling delay). Lower target for scaling headroom.
5. Apply
Review HPA settings + history quarterly. ZopNight recommendations highlight under/over-provisioning patterns. Tune top 3 workloads each quarter; pattern compounds.
For your team: monthly HPA review on the top 5 highest-cost workloads. Address Patterns 1, 2, 3 in priority order.
Related lessons
- L1: Requests and limits
- L3: Idle workloads (next)
- L4: Single-replica patterns
- L5: Security signals from K8s cost
- L6: Orphan PVC cleanup
- M5.2.L2: Scale-to-one weekends
Glossary terms touched
HPA (Horizontal Pod Autoscaler) · ScalingLimited condition · HPA target · HPA stabilization window