Abstract
Kubernetes provides two primary mechanisms for automatic workload scaling: the Horizontal Pod Autoscaler (HPA), which adjusts the number of pod replicas in response to observed metrics, and the Vertical Pod Autoscaler (VPA), which adjusts the CPU and memory requests on individual pods. Teams often treat these as alternatives when they are more accurately described as complementary tools with different tradeoffs. Understanding when each mechanism is appropriate, and when they conflict, is one of the less documented but practically important aspects of running production workloads on Kubernetes.
HPA Behavior and Limits
HPA watches a metrics source, typically the Kubernetes Metrics Server for CPU utilization or a custom metrics adapter for application-level signals, and scales the replica count up or down to maintain a target value. The scaling algorithm is straightforward: desired replicas equals ceil(current replicas multiplied by current metric divided by target metric). The challenge is that HPA responds to average utilization across all replicas. If one pod is CPU-saturated and three others are idle, the average may look acceptable and HPA will not scale out. Tuning the target utilization conservatively (for example, targeting 60% CPU rather than 80%) provides headroom for this averaging effect and for burst traffic that arrives faster than the scale-out latency, which is typically 15 to 60 seconds depending on the polling interval and cluster conditions.
VPA Behavior and the Restart Problem
VPA monitors actual resource usage over time and updates the requests field in the pod spec. Since pod resource requests are immutable after creation, VPA must evict and restart pods to apply new recommendations. This is the fundamental limitation that makes VPA inappropriate for stateless high-traffic services where a restart causes a brief availability impact. VPA is most useful for batch jobs, background workers, and services with predictable and relatively stable resource needs where right-sizing reduces waste on overprovisioned instances. The VPA Recommender operates in three modes: Off (recommendations computed but not applied), Initial (recommendations applied only at pod creation), and Auto (recommendations applied via eviction). Most production teams start with Off or Initial to validate recommendations before enabling Auto.
Conflicts When Running Both Together
Running HPA and VPA on the same Deployment is explicitly unsupported when both are targeting CPU metrics, because they will fight each other: HPA scales out when CPU is high, VPA evicts pods to apply new CPU requests, evictions trigger pod restarts that temporarily reduce capacity, and HPA sees the load spike from reduced capacity and scales out again. The supported pattern is to use HPA with CPU metrics and VPA in Recommender mode only, using VPA recommendations to inform manual updates to the pod spec rather than applying them automatically. Alternatively, use HPA with a custom metrics target that is not CPU (such as queue depth or request rate) while allowing VPA to manage CPU and memory requests in Auto mode, since the two autoscalers are then operating on non-overlapping signals.
KEDA as a Complement
KEDA (Kubernetes Event-Driven Autoscaler) extends HPA by adding over 50 built-in scalers for external event sources - SQS queue depth, Kafka consumer lag, Redis list length, and many others. It does not replace the HPA-VPA tradeoff decision; it expands the set of signals available to HPA-style horizontal scaling. For workloads where throughput is driven by an upstream queue rather than by CPU or request rate, KEDA’s queue-depth scaler is typically more precise than CPU-based HPA and avoids the underprediction problem that comes from averaging CPU utilization across a pool of unevenly loaded pods.