HPA vs VPA: When to Use Each, and Can You Use Both? Kubernetes users must choose between Horizontal Pod Autoscaler (HPA) and Vertical Pod Autoscaler (VPA) based on workload characteristics, with HPA suited for stateless services and VPA for rightsizing stateful or resource-sensitive applications. The two can be used together safely by separating metrics, such as using HPA on request rate and VPA in Off mode. Cast AI offers an alternative that rightsizes pods in-place without evictions, including for DaemonSets which VPA does not support. Key Takeaways - HPA adds or removes pod replicas based on load. VPA adjusts CPU and memory requests per pod. These solve two different autoscaling problems. - HPA fits stateless workloads: web APIs, microservices, and traffic-variable jobs that benefit from running more instances under load. - VPA fits rightsizing use cases: stateful apps, JVM services, and ML inference servers where adding replicas doesn’t address the root problem. - Running both safely requires metric separation. HPA on RPS or queue depth with VPA in Off mode works. HPA on CPU while VPA also controls CPU breaks. - Cast AI Workload Optimization rightsizes pods in-place via Linux cgroup modification, without evictions, and supports DaemonSets – which VPA’s Updater does not touch. HPA vs VPA: A Side-by-Side Comparison Choosing between HPA and VPA starts with understanding what each one scales. HPA responds to traffic by adding or removing pod replicas – a horizontal solution for workloads that run in parallel. VPA fine-tunes individual pod resource requests – a vertical solution for getting per-pod allocation right. | HPA | VPA | | |---|---|---| What it scales | Pod replica count | CPU and memory requests per pod | How it works | Controller adjusts Deployment or StatefulSet replicas every 15 seconds | Recommender, Updater, and Admission Controller adjust resource specs | Metric types | CPU, memory, custom metrics, external metrics | Historical CPU and memory usage per container | Update mode | Continuous near-real-time scaling | Off, Initial, Recreate, InPlaceOrRecreate K8s 1.33+ | Disruption risk | Low – adds or removes replicas without touching running pods | Medium to high with Recreate; low with InPlaceOrRecreate on K8s 1.33+ | Best for | Stateless workloads, variable traffic patterns | Rightsizing requests, stateful apps, JVM and ML workloads | Limitations | Requires resource requests set; no native scale-to-zero | Needs separate install; requires 24-48h of data; Recreate mode causes restarts; no DaemonSet support | Kubernetes requirement | Built-in autoscaling/v2 stable since K8s 1.26 | External CRD from kubernetes/autoscaler repo | When to Use HPA HPA is the right tool when your workload scales by running more instances. Stateless services – web APIs, REST microservices, and gRPC servers – fit this model well. The HPA controller checks metrics every 15 seconds and adjusts replica count using this formula: desiredReplicas = ceil currentReplicas x currentMetricValue / desiredMetricValue HPA ignores metric deviations within 10% of the target the default tolerance window to prevent constant scaling on minor fluctuations. Tune this with --horizontal-pod-autoscaler-tolerance on the controller. For HPA to function, metrics-server must run in the cluster and resource requests must be set on the container spec – without requests, HPA has no baseline for CPU percentage calculations. Here is a production-ready HPA manifest using autoscaling/v2 : apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: api-server-hpa namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: api-server minReplicas: 2 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 10 periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 0 policies: - type: Pods value: 4 periodSeconds: 60 - type: Percent value: 100 periodSeconds: 60 selectPolicy: Max The scaleDown stabilization window prevents thrashing when utilization briefly dips between bursts. The scaleUp policy caps scale-up at the larger of 4 pods or 100% of current replicas per minute, preventing a traffic spike from overwhelming downstream services. Queue Consumers and KEDA Queue consumers fit HPA well, but native HPA cannot scale to zero when a queue empties and only supports CPU and memory metrics natively. KEDA adds 60+ external metric sources – Kafka lag, SQS queue depth, Prometheus queries – and enables scale-to-zero. For event-driven workloads, evaluate KEDA alongside native HPA. For a deeper look at HPA mechanics and cost implications, see How HPA Can Help You Save on the Cloud https://cast.ai/blog/what-is-kubernetes-hpa-and-how-can-it-help-you-save-on-the-cloud/ . When to Use VPA VPA answers a different question: are your pod resource requests actually correct? According to the Cast AI 2026 State of Kubernetes Optimization Report, 69% of requested CPU goes unused across production clusters, average utilization sits at 8%, and memory overprovisioning reaches 79%. Teams set conservative requests at deployment, workloads stabilize, and nobody revisits the numbers. VPA’s Recommender watches historical usage and generates resource suggestions. Three workload types get the most value from it: Stateful Workloads, JVM Services, and ML Inference Stateful workloads that can’t scale horizontally. A single-replica database or cache cannot simply add replicas to handle load. VPA adjusts requests so the pod receives the CPU and memory it actually uses rather than what was guessed at deployment time. One gotcha: VPA’s Updater skips pods when a Deployment has only 1 replica, because evicting the single pod would cause downtime. To override this default, set --min-replicas=1 on the VPA Updater. Alternatively, use updateMode: Initial for zero-disruption recommendations on startup only. JVM-based services. Java heap behavior makes resource sizing difficult to estimate upfront. VPA observes real usage and adjusts accordingly, typically stabilizing after 24-48 hours of data collection. ML inference servers. Model serving carries variable memory footprints depending on batch size and model weight loading. VPA rightsizes these pods based on observed patterns rather than worst-case estimates. Note: VPA is not built into Kubernetes. Install it from the kubernetes/autoscaler GitHub repository. It adds three components: Recommender generates suggestions , Updater evicts pods needing resource changes , and Admission Controller applies new requests at pod creation . DaemonSet limitation: VPA does not support DaemonSets. For DaemonSet workloads, set resource requests manually or use VPA in Off mode to gather baseline data without automated changes. VPA Update Modes VPA offers four update modes. Choosing the right one matters for production stability: Off : Generates recommendations but makes no changes. Use this for observation before committing to automated updates. Initial : Applies recommendations only at pod creation. Running pods remain unchanged. This works for workloads with frequent restarts. Recreate : Evicts pods when requests need adjustment and recreates them with updated specs. Configure a PodDisruptionBudget PDB before enabling this in production. Without a PDB, VPA can evict all pods simultaneously. A PDB with minAvailable: 1 ensures at least one pod stays available. InPlaceOrRecreate : Introduced in K8s 1.33. Applies changes in-place when supported, falls back to Recreate when not. GA expected in K8s 1.35. Start with Off Mode Before enabling automated updates, run VPA in Off mode to collect recommendations without touching running pods. Here is a minimal example: apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: payment-api-vpa namespace: production spec: targetRef: apiVersion: apps/v1 kind: Deployment name: payment-api updatePolicy: updateMode: "Off" Inspect VPA Recommendations Check VPA-generated recommendations kubectl describe vpa