Kubernetes VPA: Rightsize Pod Resource Requests

Alberto Grande
Head of Marketing

Kubernetes makes it easy to run workloads in containers, but setting the right CPU and memory requests is still a guessing game. Over-provisioned pods waste resources. Under-provisioned pods get throttled—or worse, evicted.
The Kubernetes Vertical Pod Autoscaler — Kubernetes VPA — helps solve this. It automatically adjusts CPU and memory requests and limits for your pods based on observed usage. Instead of scaling out like HPA, VPA scales up or down the resources each pod needs.
This guide is part of our autoscaling series and focuses specifically on how VPA works, when to use it, what limitations to watch for, and how to go beyond it with real-time, cost-aware optimization.
Kubernetes Autoscaling Series:
- Kubernetes Autoscaling
- Horizontal Pod Autoscaler (HPA)
- Vertical Pod Autoscaler (VPA)
- Cluster Autoscaler (CA)
What VPA Does and Does Not Do#
VPA is narrower than most teams expect when they first reach for it. Before the mechanics, the boundary:
What VPA does:
- Observes historical CPU and memory usage per container and derives recommended requests.
- Applies those recommendations automatically, in its
AutoandRecreateupdate modes. - Sets both requests and limits, maintaining the ratio between them.
- Works per-container, so a multi-container pod gets independent recommendations.
What VPA does not do:
- It does not add or remove replicas. That is HPA's job, and pointing both at the same resource is the single most common way teams break their own autoscaling.
- It does not classically resize a pod in place. Its traditional path is eviction and recreation, which is why restart-sensitive workloads are excluded from it.
- It does not know anything about cost. A recommendation that is technically correct can still leave you on a more expensive node shape than you need, because node prices are not an input.
- It does not react to anything but its own usage history — not traffic forecasts, not deploy events, not queue depth.
- It does not consolidate nodes. Shrinking requests without a scheduler that repacks and removes nodes changes your resource graph and not your invoice.
That last point is the one that costs teams a quarter: rightsizing that does not translate into fewer nodes does not translate into money. VPA is a request-correction tool, not a savings tool, and the gap between those two things is where an entire product category lives.
How Kubernetes VPA Works#
The Vertical Pod Autoscaler (VPA) continuously monitors the resource usage of your pods and recommends updated CPU and memory requests. Unlike the Horizontal Pod Autoscaler (HPA), VPA doesn’t add or remove pod replicas—it adjusts the size of each pod.
VPA is composed of three components, each handling a different part of the process:
| Component | Role | Triggers |
|---|---|---|
| Recommender | Analyzes historical CPU and memory usage and generates resource suggestions | Runs continuously |
| Updater | Decides when to apply new recommendations by evicting pods | Pod lifecycle events or thresholds exceeded |
| Admission Controller | Injects recommended resources at pod creation time | Every new pod start |
Here’s how it works in practice:
- Recommender collects metrics from pods and generates target CPU/memory values.
- Updater decides whether to evict a pod to apply the recommendation (based on policies).
- Admission Controller mutates pod specs on startup to apply recommendations automatically.
⚠️ Note: If the updateMode is set to "Auto", VPA may evict and restart pods to apply new values. This can cause downtime if not planned for.
This architecture allows VPA to gradually adapt pod sizing over time, but it also means updates aren’t instant—and pod restarts can affect service stability.
When to Use VPA#
VPA is best suited for workloads where scaling out (adding replicas) isn’t effective—or where tuning resource requests manually is inefficient. It helps teams rightsize CPU and memory for individual pods, especially in systems where consistent performance depends on how much is allocated per instance.
Ideal Use Cases#
- Memory-bound applications like Java, Spark, or ML workloads
- Batch jobs that vary in resource usage over time
- Internal APIs or services where you prefer fewer, well-sized pods
- Stateful applications that can’t scale horizontally easily
- Development and test clusters where developers often guess resource requests
VPA vs HPA vs Cluster Autoscaler#
| Capability | VPA | HPA | Cluster Autoscaler (CA) |
|---|---|---|---|
| What it scales | Pod CPU/memory requests/limits | Pod replica count | Number of cluster nodes |
| Acts on live pods? | Only with eviction or restart | Yes | No (infra-level only) |
| Scaling trigger | Historical resource usage | Current CPU/memory or custom metrics | Pending pods / idle nodes |
| Can it downscale? | Yes (with restarts) | Yes | Yes |
| Works with stateful apps? | Yes | ⚠️ Limited | ✅ Yes |
| Main benefit | Rightsizes containers | Scales out with load | Optimizes node-level capacity |
If your workload is CPU-light but memory-heavy—or if you’re constantly adjusting requests to avoid throttling or OOM kills—VPA may be a better fit than HPA. Just keep in mind that updates often require a pod restart, so it’s best used when downtime is acceptable or easily mitigated.
Limitations and Trade-offs#
While the Vertical Pod Autoscaler (VPA) solves important problems—like reducing over-provisioning and automating resource tuning—it also comes with trade-offs that make it unsuitable for certain workloads or setups.
Applying a Recommendation Usually Means Restarting the Pod#
VPA's classic update path cannot change a running pod. To apply new resource requests it evicts and recreates the pod. This creates a few challenges:
- Stateful or long-lived apps may experience downtime
- Pods using emptyDir or non-persistent storage lose data on restart
- If the app isn’t restart-friendly, updates can introduce risk
This is why many teams run VPA in updateMode: "Off" to collect recommendations first, then apply them manually.
The Kubernetes primitive for avoiding this now exists — the /resize subresource lets a controller patch CPU and memory on a running pod rather than replacing it. But having the primitive available and using it safely in production are different problems, and the gap between them is larger than it looks — In-Place Vertical Scaling below covers what that actually involves.
Conflicts with HPA#
VPA and HPA don’t work well together if both are configured to manage the same resource, like CPU or memory. Kubernetes doesn’t resolve conflicts—it just creates unpredictable behavior.
Safe patterns include:
- HPA for replicas, VPA for memory only
- Or using VPA in recommendation mode alongside HPA for scaling
Limited Signal Awareness#
VPA only uses historical CPU and memory usage to generate recommendations. It does not consider:
- Request rate
- Latency
- I/O
- Business-level metrics
This makes it less effective for:
- Highly dynamic workloads
- Latency-sensitive systems where usage ≠ demand
Metrics Need Time to Stabilize#
VPA relies on aggregated metrics. Short-lived or bursty pods may not generate enough consistent data for meaningful recommendations.
Summary#
VPA is a powerful tool for container rightsizing—but it’s not a drop-in solution. It’s best deployed with awareness of pod lifecycle, scaling strategy, and observability needs.
In-Place Vertical Scaling: What It Actually Takes#
Most writing about in-place pod resize stops at "Kubernetes can now do this." The interesting part is what happens when it refuses, because that is most of the engineering.
We run in-place resize in production through our Write Operator (internally, tunr), which patches CPU and memory on a running pod via the /resize subresource when policy allows it and the cluster meets the version and feature-gate requirements. Here is what we have learned from the rejection paths.
A resize can be refused at two different points, by two different components.
The first is a preflight check, before anything reaches the API server. It rejects the resize if applying it would change the pod's QoS class, or if it touches a resource whose resizePolicy on that container does not permit the change — for example decreasing a memory limit on a container whose memory resizePolicy is not RestartContainer. Catching this locally matters: it is cheaper to reject a resize you know the API server will refuse than to send it and interpret the failure.
The second is the kubelet, after the API server has already accepted the patch. This is the one that surprises people, because "the API server said yes" is not the same as "the resize happened."
Kubelet rejections come in three flavors, not two. The distinction determines whether retrying is worth anything:
| Rejection | What it means | Retryable |
|---|---|---|
| Infeasible | The node can never satisfy this request — typically the requests exceed the node's allocatable. | No. Retrying on the same node is pointless. |
| Deferred | The node cannot satisfy it right now — node pressure, pending evictions, in-flight allocation. | Yes. Conditions may change. |
| Actuation error | From Kubernetes 1.35 onward: the API server accepted a memory-limit decrease, but the kubelet holds it because current usage is still above the new limit. | Yes. Usage may fall. |
That third case only exists on newer clusters, and it is easy to misread as a hard failure. It is not — it is the kubelet declining to shrink a limit out from under a container that is still using the memory.
What we do about it. Deferred and actuation-error rejections are retried on a bounded backoff — 30 seconds, then 1 minute, then 2 minutes — each attempt sitting on top of the operator's own per-attempt poll budget for the resize to land. Infeasible skips the retry window entirely.
When the retries are exhausted, or immediately for Infeasible, the reconciler falls back to rolling recreation so the corrected resources are still applied, and records the specific rejection reason on the recommendation's status. The workload gets its right-sized values either way; the in-place path is an optimization on how, not a precondition for whether.
That fallback is the part worth copying. A resize implementation that treats kubelet rejection as terminal leaves workloads sitting at their old, wrong values indefinitely — the recommendation is generated, refused, and quietly dropped. Bounded retry followed by a guaranteed fallback means a rejection costs you a restart, not a correction.
Source: DevZero Write Operator (
tunr). The rejection sentinels and reason constants live inoperators/tunr/internal/operator/service/inplace_resize.go; the retry-then-fallback logic isWorkloadRecommendationReconciler.inPlaceResizeWithFallbackinworkload_recommendation_controller.go. Verified against operator source; behavior outside the in-place resize path may differ.
Installing and Configuring VPA#
VPA is not included in Kubernetes by default—you’ll need to deploy it as a set of components maintained by the SIG Autoscaling group. Setup is straightforward, but configuration choices (especially update modes) will affect how safely VPA operates.
Step 1: Install VPA Components#
You can deploy the official Vertical Pod Autoscaler using the manifests from theVPA GitHub repo:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/autoscaler/master/vertical-pod-autoscaler/deploy/vpa-upstream.yaml
This installs the three core components:
- vpa-recommender
- vpa-updater
- vpa-admission-controller
Make sure your cluster has:
- Metrics Server installed and working
- RBAC enabled
- Webhooks enabled (for the Admission Controller)
Step 2: Create a Deployment#
Here’s a basic deployment using static resource requests:
Step 3: Attach a VPA Resource#
Now define the VerticalPodAutoscaler object to manage the resource requests.
Update Modes Explained#
| Mode | What it does | When to use it |
|---|---|---|
| Off | Only generates recommendations (no action taken) | Safest for testing and monitoring |
| Auto | Automatically evicts pods to apply new values | Use in non-critical workloads or dev envs |
| Recreate | Applies changes on next pod restart (manual trigger) | Useful if you want to control timing |
🛑 Be cautious with "Auto" mode in production—it may restart pods at inconvenient times.
Once installed, VPA runs continuously in the background and updates recommendations as it observes usage patterns.
Best Practices for Using VPA in Production#
Running VPA in production requires careful planning. It’s not just about enabling autoscaling—it’s about controlling when and how resource updates happen, and avoiding disruptions in critical workloads.
1. Start in Observation Mode (updateMode: "Off")#
Begin with VPA in passive mode to collect recommendations without applying changes. This lets you:
- Validate whether your resource requests are misaligned
- Understand usage patterns before taking action
- Avoid surprises in production
Use this data to rightsize manually, or switch to automated modes once you’re confident.
2. Choose the Right Update Mode#
Here’s a quick breakdown of when to use each mode:
| Mode | Applies Changes Automatically? | Causes Pod Restarts? | Recommended For |
|---|---|---|---|
| Off | No | No | Baseline visibility, production clusters |
| Auto | Yes | Yes | Non-critical workloads, dev/test environments |
| Recreate | No (applied on next restart) | Controlled | Stateful apps, canary or rolling deploys |
3. Avoid Conflicts with HPA#
If you’re using both VPA and HPA:
- Do not target the same resource (e.g. CPU)
- Safe pattern: HPA scales replicas based on CPU; VPA adjusts memory requests only
- Alternatively, use VPA in Off mode for recommendations while HPA handles live scaling
4. Combine VPA with Cluster Autoscaler#
VPA can reduce resource requests, which allows the Cluster Autoscaler to pack more pods onto fewer nodes. This improves binpacking and can reduce cloud spend—especially in clusters with mixed workloads.
But remember: if VPA suddenly increases resource requests, pods may go unschedulable unless the Cluster Autoscaler is fast enough to provision space.
5. Monitor Impact with Cost and Usage Metrics#
Adjusting resource requests affects:
- Node binpacking efficiency
- Pod priority and scheduling
- Overall cluster cost
It’s important to track how VPA decisions translate to infrastructure behavior. This is where aKubernetes cost monitoring tool helps—by connecting usage changes to real spend.
Advanced VPA Use Cases#
VPA is often treated as a basic resource tuning tool—but it can also support more advanced scenarios, especially when combined with observability and deployment automation.
1. Memory-Bound or ML Workloads#
Machine learning jobs and JVM-based services (like Spark, Java, or Scala) often don’t scale well horizontally. They need:
- High memory per pod
- Stable performance across execution cycles
VPA helps here by gradually learning resource profiles over time. It allows teams to:
- Avoid manual tuning per job run
- Reduce OOM kills and inefficient over-provisioning
- Adapt to seasonal or dataset-based memory usage shifts
2. Batch Jobs and CronJobs#
Short-lived jobs often have unpredictable spikes in resource use. VPA can:
- Recommend requests based on past executions
- Allow tighter binpacking across job waves
- Work well with updateMode: "Recreate" for predictable deployment cycles
If you’re running time-sensitive ETL, data prep, or distributed compute jobs, VPA helps avoid both under- and over-resourcing.
3. Scheduled Resource Resetting#
Some teams use VPA to reset resource requests during off-peak hours:
- Run VPA in Auto mode during maintenance windows
- Let it update requests and evict pods without user impact
- Switch back to Off mode during peak hours
This hybrid approach blends automation with operational control—especially useful for clusters with strict uptime or compliance requirements.
4. VPA + Observability Tools#
VPA only acts on CPU and memory metrics. But when paired with observability platforms, you can:
- Validate VPA behavior against latency and SLOs
- Flag over-aggressive recommendations
- Feed insights into custom dashboards or cost analysis tools
If you use something like Prometheus + Grafana or a Kubernetes cost optimization tool, this unlocks much deeper tuning.
Alternatives to VPA#
If the limitations above are disqualifying for your workloads — and for restart-sensitive or stateful services they usually are — the practical question becomes which replacement to run rather than whether to run one.
The short version of the landscape:
- Stay on native VPA in
updateMode: "Off"and treat it as a recommendation engine, applying changes through your normal deploy process. Free, safe, and entirely manual — which stops scaling somewhere around a few hundred workloads. - A managed rightsizing platform that applies changes continuously, ideally in place. This is the category DevZero, ScaleOps, Cast AI, StormForge, PerfectScale and Sedai compete in, and they differ mostly in whether they restart your pods, whether they touch nodes as well as pods, and whether they can be run self-hosted.
- Roll your own on top of the
/resizesubresource. Viable, and the section above is a fair preview of the edge cases you are signing up for.
We maintain a detailed head-to-head of the managed options, including where each one wins and where DevZero is the wrong choice:
→ Best Kubernetes VPA Alternatives for Production in 2026
Operational Constraints of VPA#
The Vertical Pod Autoscaler helps solve a common Kubernetes problem: poorly sized workloads. It analyzes historical CPU and memory usage and recommends better resource requests—reducing over-provisioning and manual tuning.
But while useful, VPA has real limitations when used in production. It isn’t designed for real-time responsiveness, introduces disruption when applying changes, and lacks broader context like cost or scheduling efficiency. These constraints make it helpful for offline recommendations—but difficult to rely on for live, automated optimization.
DevZero extends the same intent behind VPA, but addresses these operational gaps.
- Workload Rightsizing: VPA suggests better resource values but requires restarts to apply them. DevZero adjusts CPU and memory requests on running pods, in real time, without evictions. This eliminates the downtime and complexity associated with production resizing.
- Live Migration: One of the biggest risks of VPA is that it triggers restarts. DevZero safely migrates workloads across nodes by pausing and resuming execution—avoiding cold starts and service disruption during optimization cycles.
- Binpacking: VPA reduces pod size, which helps indirectly with binpacking—but DevZero goes further. It actively redistributes workloads across nodes based on updated resource profiles, improving density and reducing the number of active nodes needed.
- Visibility into cost impact: VPA has no awareness of infrastructure cost. DevZero ties resource decisions to actual spend, so platform teams can see how changes affect node utilization and cloud cost—closing the loop between tuning and business impact.
In short, VPA shows you what to fix—DevZero makes it actionable, safe, and continuous. It’s the next step for teams that want the benefits of autoscaling without the operational trade-offs.
See how DevZero rightsizes without restarts →

Alberto Grande
Head of Marketing

