The Kubernetes Vertical Pod Autoscaler (VPA) automatically adjusts the CPU and memory requests of pods based on historical usage. When it works correctly, it eliminates the guesswork from resource sizing — over-provisioned pods get trimmed, starved pods get more headroom, and OOM kills drop. When VPA silently fails, pods continue running with stale or incorrect resource requests, and the OOM events and throttling that VPA was supposed to prevent return unnoticed.
This guide covers how to monitor VPA's three core components — the Recommender, Updater, and Admission Controller — and how to use Vigilmon for external visibility into your resource optimization infrastructure.
VPA Architecture: What Can Go Wrong
VPA has three distinct processes, each of which can fail independently:
- Recommender — reads historical metrics from the Metrics Server or Prometheus and produces
LowerBound/UpperBound/Targetresource recommendations. If this fails, VPA recommendations go stale. - Updater — checks running pods against current recommendations and evicts pods that are outside recommendation bounds so they restart with updated requests. If this fails, VPA recommendations exist but are never applied.
- Admission Controller — a Mutating Admission Webhook that rewrites resource requests at pod creation time. If this fails, new pods start with their original (uncorrected) requests.
A monitoring gap in any one of these lets incorrect resource sizing persist indefinitely.
Monitoring the VPA Recommender
Recommender Pod Health
# Check Recommender pod status
kubectl get deployment vpa-recommender -n kube-system
# View recent Recommender logs
kubectl logs -n kube-system -l app=vpa-recommender --since=1h | tail -30
# Check for Recommender errors
kubectl logs -n kube-system -l app=vpa-recommender --since=1h | grep -i "error\|warn\|fail"
Recommendation Freshness
VPA stores recommendations in the VerticalPodAutoscaler custom resource status. Check whether recommendations are being updated:
# View current recommendations across all VPAs
kubectl get vpa -A -o json | jq -r \
'.items[] | "\(.metadata.namespace)/\(.metadata.name): lastUpdated=\(.status.recommendation.containerRecommendations[0].uncappedTarget // "none")"'
# Check if any VPA has no recommendation yet (Recommender may be broken)
kubectl get vpa -A -o json | jq -r \
'.items[] | select(.status.recommendation == null) | "\(.metadata.namespace)/\(.metadata.name): NO RECOMMENDATION"'
A script to alert on stale recommendations:
#!/bin/sh
# Alert if any VPA recommendation hasn't been updated in 2 hours
kubectl get vpa -A -o json | jq -r '.items[] | .metadata.name' | while read VPA_NAME; do
LAST_TRANSITION=$(kubectl get vpa "$VPA_NAME" -o jsonpath='{.status.conditions[?(@.type=="RecommendationProvided")].lastTransitionTime}' 2>/dev/null)
if [ -z "$LAST_TRANSITION" ]; then
echo "WARNING: VPA $VPA_NAME has no RecommendationProvided condition"
fi
done
Recommender Prometheus Metrics
The VPA Recommender exposes metrics on port 8942:
# Recommendations currently provided
vpa_recommender_recommendations_provided
# How many container specs have recommendations
vpa_recommender_containers_covered
# Metric fetch failures (Recommender can't get usage data)
vpa_recommender_recommendation_process_duration_seconds_count
# Port-forward to check Recommender metrics
kubectl port-forward -n kube-system deployment/vpa-recommender 8942:8942 &
curl -s http://localhost:8942/metrics | grep vpa_recommender
Monitoring the VPA Updater
Updater Pod Health and Eviction Activity
The Updater is responsible for evicting pods so they restart with corrected resource requests:
# Check Updater pod status
kubectl get deployment vpa-updater -n kube-system
# View Updater logs — look for eviction events
kubectl logs -n kube-system -l app=vpa-updater --since=1h | grep -E "(evict|Evict|error|Error)"
Expected healthy output includes lines like:
I0601 10:00:05 updater.go:160] "Successfully evicted pod" pod="production/api-6c8d9f-xz1q" vpa="production/api-vpa"
Eviction Rate Tracking
Monitor eviction rates to distinguish healthy recalibration from runaway churn:
# Count VPA-driven evictions in the last hour
kubectl get events -A \
--field-selector reason=EvictedByVPA \
--sort-by='.lastTimestamp' | wc -l
# Watch evictions in real time
kubectl get events -A \
--field-selector reason=EvictedByVPA \
-w
OOM Event Reduction Validation
VPA's core promise is to eliminate OOM kills. Track whether OOM events are decreasing after VPA is applied:
# Count OOMKilled events cluster-wide
kubectl get events -A \
--field-selector reason=OOMKilling \
--sort-by='.lastTimestamp' | tail -20
# Check containers that have been OOMKilled recently
kubectl get pods -A -o json | jq -r \
'.items[] | .metadata.namespace as $ns | .metadata.name as $pod |
.status.containerStatuses[]? |
select(.lastState.terminated.reason == "OOMKilled") |
"\($ns)/\($pod)/\(.name): last OOM at \(.lastState.terminated.finishedAt)"'
Alert if OOM events increase after VPA is deployed — this indicates the Recommender is underestimating resource needs:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: vpa-alerts
namespace: kube-system
spec:
groups:
- name: vpa
rules:
- alert: VPARecommenderDown
expr: up{job="vpa-recommender"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "VPA Recommender is down"
description: "The VPA Recommender has been unreachable for 5 minutes. Recommendations are no longer being updated."
- alert: VPAUpdaterDown
expr: up{job="vpa-updater"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "VPA Updater is down"
description: "The VPA Updater has been unreachable for 5 minutes. Existing recommendations are not being applied."
- alert: VPAHighEvictionRate
expr: increase(vpa_updater_evictions_total[30m]) > 20
for: 5m
labels:
severity: warning
annotations:
summary: "VPA is evicting pods at an unusually high rate"
description: "More than 20 VPA-driven evictions in 30 minutes may indicate unstable recommendations or misconfigured min/max bounds."
- alert: VPANoRecommendations
expr: vpa_recommender_recommendations_provided == 0
for: 30m
labels:
severity: warning
annotations:
summary: "VPA Recommender is producing no recommendations"
description: "No VPA recommendations have been generated in 30 minutes. Check Metrics Server connectivity."
Monitoring the VPA Admission Controller
The Admission Controller is a Mutating Webhook — if it becomes unavailable, new pod creation fails or falls back to original resource requests depending on your failurePolicy setting.
Webhook Health
# Check Admission Controller pod status
kubectl get deployment vpa-admission-controller -n kube-system
# Verify the MutatingWebhookConfiguration is registered
kubectl get mutatingwebhookconfigurations | grep vpa
# Describe the webhook to check its endpoint and failure policy
kubectl describe mutatingwebhookconfiguration vpa-webhook-config
Webhook Response Time
A slow Admission Controller adds latency to pod scheduling. Monitor it:
# Check recent webhook-related events
kubectl get events -A \
--field-selector reason=FailedCreate \
--sort-by='.lastTimestamp' | grep -i vpa | tail -10
# Look for admission webhook timeout errors in API server logs
# (requires access to control plane logs)
kubectl logs -n kube-system kube-apiserver-$(hostname) 2>/dev/null | \
grep "vpa" | grep -i "timeout\|error" | tail -20
End-to-End Admission Validation
Deploy a test pod and confirm VPA mutated its resource requests:
# Create a test pod in a namespace with VPA
kubectl run vpa-test --image=nginx --restart=Never -n production -- sleep 3600
# Check if VPA mutated the resource requests
kubectl get pod vpa-test -n production \
-o jsonpath='{.spec.containers[0].resources}' | jq .
# Expected: resources will include requests set by VPA, not the original spec
kubectl delete pod vpa-test -n production
Integrating Vigilmon for Resource Optimization Infrastructure Monitoring
VPA is internal cluster infrastructure — but you can monitor its health signals externally with Vigilmon:
Monitor your VPA status API or health endpoint. If you expose a simple health endpoint that queries VPA status, Vigilmon checks it from outside the cluster every minute:
- Log in to Vigilmon and go to Monitors → New Monitor.
- Add an HTTP / HTTPS monitor targeting your VPA health endpoint.
- Set check interval to 1 minute.
- Configure a keyword assertion for
"recommender":"healthy"or similar JSON field from your status API. - Set up alert channels (email, Slack, PagerDuty) so your platform team is notified immediately when VPA infrastructure degrades.
A minimal VPA health API deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vpa-health-exporter
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: vpa-health-exporter
template:
metadata:
labels:
app: vpa-health-exporter
spec:
serviceAccountName: vpa-health-reader
containers:
- name: exporter
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
while true; do
RECOMMENDER=$(kubectl get deployment vpa-recommender -n kube-system \
-o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo 0)
UPDATER=$(kubectl get deployment vpa-updater -n kube-system \
-o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo 0)
ADMISSION=$(kubectl get deployment vpa-admission-controller -n kube-system \
-o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo 0)
echo "{\"recommender\":${RECOMMENDER},\"updater\":${UPDATER},\"admission\":${ADMISSION}}" > /tmp/status.json
sleep 30
done &
python3 -m http.server 8080 --directory /tmp
ports:
- containerPort: 8080
Vigilmon will alert you if this endpoint becomes unreachable — catching node failures, namespace deletions, or RBAC regressions that kill VPA infrastructure before they affect workload sizing.
Key Metrics Summary
| Component | What to Monitor | Alert Threshold |
|-----------|----------------|-----------------|
| Recommender pod | up{job="vpa-recommender"} | Down for > 5m |
| Updater pod | up{job="vpa-updater"} | Down for > 5m |
| Admission Controller | Pod readiness | 0 ready for > 5m |
| Recommendations provided | vpa_recommender_recommendations_provided | 0 for > 30m |
| Eviction rate | vpa_updater_evictions_total | > 20 in 30m |
| OOM events | kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} | Any increase post-VPA |
| VPA objects with no recommendation | kubectl get vpa | Any VPA with null status |
Conclusion
VPA's three-component architecture means a failure in any one layer silently degrades your resource optimization. The Recommender going stale means recommendations don't update; the Updater going down means updates aren't applied; the Admission Controller failing means new pods ignore VPA entirely. By monitoring each component with targeted health checks, eviction rate tracking, and OOM event reduction validation, you catch VPA failures before they manifest as throttling, OOM kills, or resource waste. Vigilmon's external monitoring rounds out the picture by giving you an out-of-cluster perspective on whether your VPA infrastructure is alive and reachable.