Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Vertical Pod Autoscaler (VPA) Explained

By Kokil Thapa | Last reviewed: September 2026

Setting accurate CPU and memory requests is one of the hardest operational challenges in Kubernetes, yet getting it wrong leads directly to wasted budget or application crashes. The Vertical Pod Autoscaler (VPA) solves this by analyzing historical usage and automatically adjusting resource requests to match actual demand. Unlike horizontal scaling which adds more replicas, VPA optimizes the size of existing pods to improve cluster efficiency and stability.

If you are managing infrastructure for cost-sensitive projects, understanding the distinction between vertical and horizontal scaling is critical before touching production manifests. While I often discuss horizontal pod autoscaling in Kubernetes for handling traffic spikes, VPA serves a different purpose: long-term efficiency optimization. Before deploying VPA, ensure you have a solid grasp of Kubernetes resource limits and requests, as VPA modifies these values directly and misconfiguration can cause immediate outages.

How does Vertical Pod Autoscaler (VPA) actually work?

VPA is not a single controller but a system of three distinct components that form a feedback loop. Understanding this architecture prevents the most common debugging failures when recommendations seem stale or updates fail to apply.

VPA Component ArchitectureRecommenderFetches Metrics APIComputes TargetStores RecommendationUpdaterWatches VPA ObjectsEvicts Outdated PodsTriggers RecreationAdmission PluginIntercepts CREATEInjects ResourcesBefore SchedulingMetrics Server & etcdHistorical Usage Data + VPA CRD State
VPA relies on three decoupled components: the Recommender computes targets, the Updater evicts stale pods, and the Admission Plugin injects resources at creation time.

The Recommender

This component runs as a standalone deployment and polls the Metrics Server every minute. It builds a histogram of resource usage over a configurable window (default 8 days) and calculates target percentiles. The Recommender writes its output to the VPA Custom Resource status field; it never touches running pods directly. If your Metrics Server is misconfigured or missing data, the Recommender will produce empty or zero recommendations.

The Updater

The Updater watches VPA objects and compares current pod specs against recommendations. When running in Auto or Recreate mode, it issues eviction API calls to pods whose requests deviate beyond the configured threshold. This is the component responsible for the disruptive part of VPA: it kills pods so they can be recreated with new values. In production, this means you must have proper PodDisruptionBudgets and readiness probes configured before enabling Auto mode.

The Admission Controller

This is a webhook that intercepts pod creation requests. When a new pod is scheduled (either from a fresh deployment or after an Updater eviction), the Admission Controller rewrites the resource requests in-flight based on the latest VPA recommendation. Without this component, newly created pods would use the original manifest values until the next Updater cycle. This component is mandatory for Initial and Auto modes to function correctly.

What are the VPA update modes and when should you use each?

Choosing the wrong update mode is the most frequent cause of VPA-related incidents. Each mode represents a different trade-off between automation safety and operational overhead.

ModeBehaviorPod DisruptionBest For
OffGenerates recommendations only; never modifies podsNoneInitial assessment, auditing, non-production testing
InitialSets resources only at pod creation time; no evictionsOnly on natural restartsStateful sets, databases, jobs where restarts are expensive
Auto (Recreate)Applies recommendations on create AND evicts running podsYes — proactive evictionsStateless web apps, microservices with fast startup times

Starting safely with Off mode

I always deploy VPA in Off mode first on any workload I haven't previously profiled. This lets you observe recommendations for several days without risk. Check the VPA status object:

kubectl get vpa my-app-vpa -o jsonpath='{.status.recommendation}'

If the recommended values look reasonable compared to your monitoring dashboards, you can graduate to Initial or Auto. On legal-tech portals I've maintained, where document processing jobs have highly variable memory profiles, spending two weeks in Off mode prevented us from setting aggressive limits that would have killed batch jobs mid-execution.

When Auto mode requires guardrails

Auto mode is powerful but destructive. The Updater will evict pods whenever usage drifts outside the update policy thresholds. Without a PodDisruptionBudget, it can evict all replicas simultaneously during a recommendation shift. Always pair Auto mode with:

  • A PDB allowing at least one unavailable pod
  • Readiness probes that prevent traffic routing until the new pod is warm
  • Graceful shutdown handlers in your application code
  • Minimum replica count of 2+ for any service receiving live traffic

How do you configure Vertical Pod Autoscaler for production workloads?

A minimal VPA manifest is deceptively simple, but production configurations require explicit boundaries to prevent runaway scaling or resource starvation.

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: laravel-api-vpa
  namespace: production
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: laravel-api
  updatePolicy:
    updateMode: "Auto"
    minReplicas: 2
  resourcePolicy:
    containerPolicies:
    - containerName: php-fpm
      minAllowed:
        cpu: 100m
        memory: 256Mi
      maxAllowed:
        cpu: 2000m
        memory: 4Gi
      controlledResources: ["cpu", "memory"]
      mode: "Auto"
    - containerName: nginx-sidecar
      mode: "Off"

Setting minAllowed and maxAllowed boundaries

Never deploy VPA without resource boundaries. The Recommender uses percentile-based algorithms that can suggest extremely low values during quiet periods or extremely high values after a transient spike. The minAllowed floor prevents the scheduler from placing pods on nodes too small to run them, while maxAllowed caps costs and prevents a single pod from consuming an entire node. For PHP-FPM containers in my experience, setting minAllowed.memory to at least 256Mi avoids OOM kills during composer autoload initialization, even if median usage suggests 180Mi is sufficient.

Excluding sidecars and init containers

VPA applies recommendations per-container. If your pod includes a logging sidecar, proxy, or cache container, explicitly set their policy to Off or define separate boundaries. Letting VPA auto-scale a Redis sidecar alongside your application container creates coupling that makes debugging nearly impossible when one gets starved.

Resource Policy BoundariesmaxAllowedminAllowedActual Usage Over TimeSafe ZoneVPA recommendations are clamped between min/max boundaries regardless of observed spikes or troughs
Resource policies clamp VPA recommendations within safe operational boundaries, preventing both OOM kills and excessive cost allocation.

Why shouldn't you combine VPA and HPA on the same metric?

This is the single most important constraint to internalize: never configure VPA and HPA to scale on the same resource metric for the same workload. Both controllers watch the same Metrics Server data and issue conflicting adjustments, creating an oscillation loop that destabilizes the cluster.

The feedback loop problem

Imagine a deployment where HPA scales on CPU utilization and VPA also manages CPU requests. When load increases, HPA adds replicas. The additional replicas distribute the work, lowering per-pod CPU usage. VPA observes lower usage and reduces requests. With smaller requests, each pod handles less work efficiently, causing utilization to spike again. HPA responds by adding more replicas. This cycle continues indefinitely, causing constant scaling churn and unpredictable performance.

Safe combination patterns

You can safely combine VPA and HPA if they operate on orthogonal dimensions:

  1. HPA on custom metrics + VPA on CPU/memory: Use HPA to scale replica count based on queue depth, request rate, or business metrics while VPA right-sizes individual pod resources.
  2. HPA on CPU + VPA on memory only: Set VPA's controlledResources to ["memory"] exclusively, letting HPA own CPU-based horizontal scaling.
  3. Separate workloads entirely: Use VPA for batch processors and stateful services; use HPA for stateless web frontends.

For a deeper comparison of when each autoscaler applies, refer to the broader Kubernetes autoscaling guide covering HPA, VPA, and Cluster Autoscaler.

What are the common pitfalls when adopting Vertical Pod Autoscaler?

VPA has been stable since 2020, but its default behaviors still surprise teams migrating from static resource definitions. These are the issues I encounter repeatedly in production audits.

Cold start penalties after eviction

When the Updater evicts a pod, the replacement starts with zero cache, empty JIT compilation buffers, and cold connection pools. For JVM applications, Laravel with opcache, or Node.js services with heavy module initialization, this can mean 30–60 seconds of degraded latency. Configure updatePolicy.controlledValues to RequestsOnly if your application is sensitive to startup latency, and ensure your load balancer respects readiness gates strictly.

OOM kills during recommendation transitions

VPA sets requests, not limits. If your deployment defines hard memory limits and VPA recommends requests near those limits, normal variance pushes the pod into OOM territory. Either remove memory limits entirely when using VPA (letting requests act as the soft boundary) or set limits to at least 2× the VPA maxAllowed value. On a recent e-commerce project, we saw repeated OOM kills because VPA recommended 1.8Gi requests against a 2Gi limit during flash sales; raising the limit to 4Gi resolved it immediately.

Ignoring the 8-day learning window

The Recommender needs approximately 8 days of metrics to produce stable recommendations. During this period, suggestions may be volatile or absent. Do not enable Auto mode on day one. Run in Off mode, monitor the recommendation quality through at least one full business cycle (including weekends or month-end processing if relevant), then graduate gradually.

VPA Mode Decision FlowStart: New WorkloadDeploy VPA in OFF modeObserve 8+ days including peak cyclesRecommendations stable?NoYesStay in OFFTune boundariesGraduate to INITIALthen AUTO if stateless
Follow this decision flow to safely adopt VPA: always validate recommendations in Off mode before enabling automated updates.

Stateful workloads and singleton pods

VPA in Auto mode is fundamentally incompatible with single-replica stateful applications. Evicting a database primary, a message broker leader, or a distributed lock holder causes immediate service disruption. For these workloads, use Initial mode exclusively, or better yet, manage resources manually with periodic review. VPA excels at stateless, horizontally replicated services where individual pod lifecycle is disposable.

Making Vertical Pod Autoscaler work in practice

Vertical Pod Autoscaler (VPA) explained properly is not about installing a controller and forgetting it; it is about establishing a continuous right-sizing discipline. Start every adoption in Off mode, define explicit resource boundaries, respect the learning window, and never let VPA and HPA fight over the same metric. When configured correctly, VPA typically recovers 15–30% of over-provisioned cluster capacity within the first month of operation.

If you are evaluating Kubernetes autoscaling for a production system and need hands-on guidance tailored to your specific workload patterns, reach out to discuss your infrastructure requirements. Getting the initial configuration right prevents weeks of troubleshooting later.

Frequently Asked Questions

VPA automatically adjusts CPU and memory requests and limits for containers based on actual usage history. Unlike HPA, it scales vertically by resizing pods rather than adding replicas.

HPA adds or removes pod replicas based on metrics like CPU utilization. VPA resizes existing pods' resource requests and limits instead, optimizing per-pod allocation without changing replica count.

Yes, but only if HPA targets custom or external metrics, not CPU/memory. Using both on the same CPU/memory metric causes conflict because VPA changes requests while HPA reads them to calculate scaling decisions.

Off mode provides recommendations only. Initial mode sets resources at pod creation but never updates running pods. Auto mode applies recommendations by evicting and recreating pods when significant drift occurs between current and recommended values.

In Auto mode, VPA evicts pods to apply new resource recommendations. This is expected behavior. Configure PodDisruptionBudgets to prevent simultaneous evictions during deployments or node maintenance windows in production clusters.

Generally no. VPA eviction disrupts stateful workloads that cannot tolerate restarts. Use VPA Off mode for recommendations only, then manually right-size StatefulSets. Reserve Auto mode for stateless deployments where restarts are acceptable.

VPA needs at least 24 hours of metrics data before producing reliable recommendations. The recommender component analyzes historical usage patterns from metrics-server. Early recommendations may be inaccurate until sufficient sampling accumulates across varied workload conditions.

VPA uses percentile-based algorithms with configurable safety margins. Set minAllowed and maxAllowed boundaries in your VPA spec to enforce floors and ceilings. Monitor OOMKilled events after enabling VPA and adjust these bounds based on observed failures.

Yes, VPA reduces excessive resource requests when historical usage shows consistent over-allocation. However, it respects minAllowed constraints you define. Without minimums, aggressive downscaling risks OOM kills during traffic spikes that exceed the observation window.

Create a VPA object targeting your worker Deployment with updateMode set to Auto or Initial. Define minAllowed matching your baseline memory footprint since Laravel workers hold framework state in memory. Queue workers often have spiky memory patterns requiring wider safety margins than HTTP containers.

VPA requires metrics-server v0.7.x or later for stable compatibility with Kubernetes 1.30+. Verify your metrics-server installation returns valid resource metrics via kubectl top nodes before deploying VPA. Missing or stale metrics cause the recommender to produce empty recommendations indefinitely.

VPA itself consumes minimal resources, typically under 100m CPU and 128Mi memory across its three components. The real cost is indirect: unnecessary evictions waste compute during pod recreation. On Nepal-hosted infrastructure billing hourly, frequent evictions during tuning can add Rs 500-2000 monthly in wasted cycles.

Check that updateMode is not set to Off. Verify the VPA targetRef matches your workload's exact kind, name, and namespace. Confirm metrics-server is healthy and returning data. Inspect the VPA status field and recommender logs for errors indicating missing metrics or selector mismatches preventing recommendation generation.

No. Apply VPA selectively to workloads with variable or poorly understood resource profiles. Stable, well-profiled services benefit more from manual right-sizing. Overusing VPA increases operational complexity and eviction risk. Start with Off mode everywhere, promote to Auto only after validating recommendations match expectations.

First check kubectl describe vpa for status conditions and recommendation values. Compare recommended against actual using kubectl top pods. Review admission-controller logs for webhook failures. Ensure resource policies do not block updates via controlledResources or containerPolicies. Test with Initial mode to isolate whether the issue is recommendation generation or enforcement.

Share this article

What I've Built

Products I Build & Run

Legal-tech and language-services platforms I designed, built and operate — each running in production for real clients across Nepal and Australia.

More in the making — I keep shipping tools for Nepal's legal, language and digital work. Got an idea worth building?

Quick Contact Options
Choose how you want to connect me: