Introduction to the Vertical Pod Autoscaler
The Vertical Pod Autoscaler (VPA) is a Kubernetes component that automatically adjusts the CPU and memory resource requests and limits for Pods in your cluster. While the Horizontal Pod Autoscaler (HPA) scales the number of replicas, the VPA scales the size of individual Pods. This means it can increase or decrease the resources allocated to a container based on historical and current usage, helping you strike a balance between performance and cost.
VPA is maintained as part of the Kubernetes Autoscaler project and operates as a set of custom controllers running inside your cluster. It relies on a Custom Resource Definition (CRD) called VerticalPodAutoscaler to define which workloads should be monitored and how aggressively they should be tuned.
Why the Vertical Pod Autoscaler Matters
Right-sizing containers is one of the hardest operational challenges in Kubernetes. Developers often guess at resource requests, leading to two common failure modes:
- Over-provisioning: Requests are set too high, wasting cluster capacity and inflating cloud costs.
- Under-provisioning: Requests are set too low, causing Pods to be throttled, evicted, or OOM-killed during traffic spikes.
VPA addresses both problems by continuously analyzing metrics from the Metrics Server and recommending optimal resource values. In its most aggressive mode, it can even evict and recreate Pods so the new instances pick up the updated requests. This makes VPA especially valuable for:
- Stateless workloads with unpredictable resource patterns.
- Teams migrating legacy applications to Kubernetes without prior performance baselines.
- Clusters where manual tuning has become unsustainable.
- Cost-optimization initiatives that require continuous right-sizing.
How VPA Works: Core Components
VPA is composed of three distinct controllers that work together. Understanding them is essential before deploying VPA in production.
1. VPA Recommender
The Recommender is the brain of the system. It consumes metrics from the Metrics Server and Prometheus (if configured), models resource usage distributions, and computes recommended CPU and memory requests. These recommendations are stored in the status field of the VerticalPodAutoscaler resource.
2. VPA Updater
The Updater watches running Pods and compares their actual requests against the Recommender's suggestions. If a Pod deviates significantly from the recommendation and the VPA policy allows it, the Updater evicts the Pod. The workload controller (Deployment, StatefulSet, etc.) then recreates the Pod with the new resources.
3. VPA Admission Controller (Mutating Webhook)
The Admission Controller is a mutating webhook that intercepts Pod creation requests. When a new Pod is about to be scheduled, the webhook rewrites its container resource requests (and optionally limits) to match the latest VPA recommendation. This is how newly created Pods immediately benefit from learned usage patterns.
Installing VPA in Your Cluster
VPA is not installed by default in most Kubernetes distributions. The official installation scripts live in the kubernetes/autoscaler repository. Before installing, ensure that the Metrics Server is running, because VPA depends on it for real-time resource metrics.
# Clone the autoscaler repository
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
# Install VPA components
./hack/vpa-up.sh
# Verify the installation
kubectl get pods -n kube-system | grep vpa
You should see three Pods: vpa-recommender, vpa-updater, and vpa-admission-controller. If any of them fail to start, check that your cluster supports admission webhooks and that the Metrics Server is healthy.
# Confirm the CRD is registered
kubectl get crd | grep verticalpodautoscaler
# Expected output:
# verticalpodautoscalers.autoscaling.k8s.io
VPA Update Modes Explained
The behavior of VPA is controlled primarily by the updateMode field. Choosing the right mode is the most important decision when configuring VPA.
- Off: VPA only computes recommendations; it never modifies Pods. Ideal for observation and baseline collection.
- Initial: VPA sets resources only at Pod creation time. Existing Pods are never evicted or modified.
- Recreate: VPA evicts and recreates Pods whenever recommendations change significantly. This causes downtime and is suitable only for workloads that tolerate interruptions.
- Auto: The default mode. Behaves like
Recreatetoday, but is designed to transition to in-place updates as that feature matures in Kubernetes.
Creating Your First VerticalPodAutoscaler
Let's start with a sample Deployment and a VPA resource that runs in Off mode. This is the safest way to begin: you collect recommendations without affecting running workloads.
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: demo-app
template:
metadata:
labels:
app: demo-app
spec:
containers:
- name: app
image: nginx:1.25
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# vpa-off.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: demo-app-vpa
namespace: default
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: demo-app
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: "*"
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: 1
memory: 1Gi
Apply both manifests and wait a few minutes for the Recommender to gather enough samples:
kubectl apply -f deployment.yaml
kubectl apply -f vpa-off.yaml
# After 5-10 minutes, inspect the recommendations
kubectl describe vpa demo-app-vpa
The Status section of the VPA resource will contain a Recommendation block similar to this:
Recommendation:
Container Recommendations:
Container Name: app
Lower Bound:
Cpu: 25m
Memory: 262144k
Target:
Cpu: 50m
Memory: 262144k
Uncapped Target:
Cpu: 50m
Memory: 262144k
Upper Bound:
Cpu: 211m
Memory: 587202560
The Target value is what VPA would apply if it were allowed to modify the Pod. Lower Bound and Upper Bound represent the confidence interval, while Uncapped Target ignores any minAllowed or maxAllowed constraints you defined.
Switching to Auto Mode
Once you are comfortable with the recommendations, you can promote VPA to actively manage resources. Update the updateMode to Auto:
# vpa-auto.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: demo-app-vpa
namespace: default
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: demo-app
updatePolicy:
updateMode: "Auto"
minReplicas: 3
resourcePolicy:
containerPolicies:
- containerName: "*"
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: 1
memory: 1Gi
controlledResources: ["cpu", "memory"]
controlledValues: RequestsAndLimits
The minReplicas field prevents the Updater from evicting Pods if the Deployment would fall below that count, which protects availability during scale-in events. The controlledValues field determines whether VPA manages only requests or both requests and limits. Setting it to RequestsOnly is a common choice when you want to preserve explicit limits set by developers.
kubectl apply -f vpa-auto.yaml
# Watch Pods get evicted and recreated with new resources
kubectl get pods -l app=demo-app -w
Using Initial Mode for Safe Rollouts
For workloads that cannot tolerate evictions, Initial mode is an excellent compromise. VPA will only adjust resources when a Pod is first created — for example, during a Deployment rollout or scale-up. Existing Pods keep their original requests until they are naturally replaced.
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: demo-app-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: demo-app
updatePolicy:
updateMode: "Initial"
This mode is particularly useful for StatefulSets, where Pod identity and storage bindings make evictions disruptive.
Combining VPA with HPA
A frequent question is whether VPA and HPA can coexist. The answer is yes, but with an important caveat: they must not act on the same resource metric. If HPA scales based on CPU utilization, and VPA also adjusts CPU requests, the two controllers will fight each other — VPA changes the denominator of the utilization ratio, causing HPA to re-evaluate and potentially thrash.
The recommended pattern is to let HPA scale on external or custom metrics (such as requests-per-second or queue depth), while VPA manages CPU and memory requests. Alternatively, use HPA for CPU-based horizontal scaling and restrict VPA to memory only:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: demo-app-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: demo-app
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: "*"
controlledResources: ["memory"]
minAllowed:
memory: 128Mi
maxAllowed:
memory: 2Gi
This configuration lets HPA own CPU-based horizontal scaling while VPA right-sizes memory independently, avoiding the feedback loop entirely.
Best Practices for Production
Start in Off Mode
Always begin with updateMode: "Off" for at least one week. This gives the Recommender time to build a statistical model of your workload across normal and peak periods. Review the Target and Upper Bound values before enabling any active mode.
Always Set minAllowed and maxAllowed
Without bounds, VPA can recommend extreme values that destabilize scheduling. A burst of traffic might cause it to recommend several CPUs, which could prevent the Pod from being scheduled on smaller nodes. Define sensible floors and ceilings based on your node sizes and budget.
Use minReplicas to Protect Availability
When using Auto or Recreate mode, always set minReplicas to at least the minimum number of Pods required to serve traffic. This prevents the Updater from evicting the last healthy Pod during a recommendation change.
Avoid VPA on Critical Single-Replica Workloads
VPA in Auto mode evicts Pods to apply new resources. A single-replica Deployment will experience downtime during each eviction. For such workloads, prefer Initial mode or increase the replica count.
Be Cautious with StatefulSets
StatefulSet Pods are bound to PersistentVolumeClaims and have stable identities. Evicting them can cause storage reattachment delays or, in some storage drivers, temporary unavailability. Use Initial mode or carefully test Auto mode in a staging environment first.
Monitor VPA Itself
VPA exposes Prometheus metrics on each component. Key metrics to watch include vpa_recommendation_target, vpa_evictions_total, and vpa_admission_webhook_request_total. A sudden spike in evictions may indicate that your minAllowed/maxAllowed bounds are too tight or that the workload is genuinely unstable.
Namespace Your VPAs Deliberately
A VPA resource only affects workloads in its own namespace. Keep VPA definitions co-located with the workloads they manage, and use GitOps tools like Argo CD or Flux to manage them alongside your application manifests. This prevents drift and ensures recommendations are reviewed through your normal change-management process.
Troubleshooting Common Issues
No Recommendations Appear
If the Status field is empty, verify that the Metrics Server is running and returning data:
kubectl top pods -n default
If this command fails, fix the Metrics Server before troubleshooting VPA. Also confirm that the Pods have been running long enough — the Recommender typically needs several minutes of samples before producing output.
Pods Are Not Being Updated
In Auto mode, the Updater only evicts Pods when the recommendation differs from the current request by a significant margin (default 15% for CPU and memory). If your requests are already close to the target, no eviction will occur. You can verify by comparing the Pod's current requests against the VPA's Target recommendation.
Admission Webhook Errors
If new Pods fail to create with webhook-related errors, check that the vpa-admission-controller Pod is healthy and that its TLS certificate is valid. The installation script generates a self-signed certificate; if it expires, rerun ./hack/vpa-up.sh to refresh it.
Conclusion
The Vertical Pod Autoscaler is a powerful tool for automating one of the most tedious aspects of Kubernetes operations: right-sizing containers. By continuously learning from real usage data and applying recommendations through a safe, policy-driven mechanism, VPA helps teams reduce waste, prevent resource-related outages, and focus on shipping features rather than tuning YAML values. The key to success with VPA is a gradual rollout — start in Off mode, validate recommendations against your knowledge of the workload, enforce sensible bounds, and only then promote to Initial or Auto mode. When combined thoughtfully with the Horizontal Pod Autoscaler and proper observability, VPA becomes a cornerstone of a mature, cost-efficient Kubernetes platform.