Reviewing Recommendations
OptiPod stores recommendations as annotations on your workloads. This guide shows you how to review, interpret, and act on these recommendations.
Finding Recommendations
Section titled “Finding Recommendations”List Workloads with Recommendations
Section titled “List Workloads with Recommendations”# List all managed workloadskubectl get deployments -A -l optipod.io/enabled=true
# Show workloads with their managing policykubectl get deployments -A \ -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,POLICY:.metadata.annotations.optipod\.io/policyView Recommendations for Specific Workload
Section titled “View Recommendations for Specific Workload”# Get all OptiPod annotationskubectl get deployment my-app -o json | \ jq '.metadata.annotations | with_entries(select(.key | startswith("optipod.io/")))'Recommendation Format
Section titled “Recommendation Format”OptiPod stores recommendations as separate annotations for each container and resource type:
annotations: # Management optipod.io/managed: "true" optipod.io/policy: "production-policy" optipod.io/last-recommendation: "2026-01-26T10:30:00Z"
# Recommendations for 'nginx' container optipod.io/recommendation.nginx.cpu-request: "250m" optipod.io/recommendation.nginx.memory-request: "512Mi" optipod.io/recommendation.nginx.cpu-limit: "500m" optipod.io/recommendation.nginx.memory-limit: "1Gi"
# Recommendations for 'sidecar' container optipod.io/recommendation.sidecar.cpu-request: "50m" optipod.io/recommendation.sidecar.memory-request: "128Mi" optipod.io/recommendation.sidecar.cpu-limit: "100m" optipod.io/recommendation.sidecar.memory-limit: "256Mi"Reviewing Recommendations
Section titled “Reviewing Recommendations”Step 1: Check Last Recommendation Time
Section titled “Step 1: Check Last Recommendation Time”Verify recommendations are recent:
kubectl get deployment my-app \ -o jsonpath='{.metadata.annotations.optipod\.io/last-recommendation}'Step 2: List All Containers with Recommendations
Section titled “Step 2: List All Containers with Recommendations”kubectl get deployment my-app -o json | \ jq -r '.metadata.annotations | keys[] | select(startswith("optipod.io/recommendation.")) | select(contains(".cpu-request")) | split(".")[1]' | sort -uStep 3: View Recommendations for Each Container
Section titled “Step 3: View Recommendations for Each Container”# For a specific container (e.g., 'nginx')echo "CPU Request: $(kubectl get deployment my-app -o jsonpath='{.metadata.annotations.optipod\.io/recommendation\.nginx\.cpu-request}')"echo "Memory Request: $(kubectl get deployment my-app -o jsonpath='{.metadata.annotations.optipod\.io/recommendation\.nginx\.memory-request}')"echo "CPU Limit: $(kubectl get deployment my-app -o jsonpath='{.metadata.annotations.optipod\.io/recommendation\.nginx\.cpu-limit}')"echo "Memory Limit: $(kubectl get deployment my-app -o jsonpath='{.metadata.annotations.optipod\.io/recommendation\.nginx\.memory-limit}')"Step 4: Compare with Current Resources
Section titled “Step 4: Compare with Current Resources”# Get current resourceskubectl get deployment my-app -o json | \ jq '.spec.template.spec.containers[] | {name, resources}'
# Get recommended resourceskubectl get deployment my-app -o json | \ jq -r '.metadata.annotations | to_entries | map(select(.key | startswith("optipod.io/recommendation."))) | group_by(.key | split(".")[1]) | map({container: .[0].key | split(".")[1], recommendations: map({(.key | split(".")[2]): .value}) | add})'Understanding Recommendations
Section titled “Understanding Recommendations”How Recommendations Are Calculated
Section titled “How Recommendations Are Calculated”OptiPod calculates recommendations based on:
- Metrics Collection: Gathers CPU and memory usage over the rolling window
- Percentile Calculation: Uses the configured percentile (P50, P90, or P99)
- Safety Factor: Applies a multiplier for headroom (default: 1.2 = 20% headroom)
- Bounds Enforcement: Ensures recommendations stay within min/max bounds
Formula:
Recommendation = Percentile(Usage) × SafetyFactorBounded by: [ResourceBounds.Min, ResourceBounds.Max]Example Calculation
Section titled “Example Calculation”Given:
- P90 CPU usage: 200m
- Safety factor: 1.2
- CPU bounds: min=10m, max=4000m
Calculation:
Recommended CPU = 200m × 1.2 = 240mWithin bounds: 10m ≤ 240m ≤ 4000m ✓Final recommendation: 240mLimits Calculation
Section titled “Limits Calculation”Limits are calculated from requests using multipliers:
CPU Limit = CPU Request × CPULimitMultiplier (default: 1.0)Memory Limit = Memory Request × MemoryLimitMultiplier (default: 1.1)Decision Framework
Section titled “Decision Framework”Use this framework to decide whether to apply a recommendation:
✅ Apply Immediately
Section titled “✅ Apply Immediately”- Change: Less than 50% reduction or less than 100% increase
- Workload: Non-critical (dev, staging, non-production)
- Metrics: 7+ days of data
- Savings: More than 30%
⚠️ Apply with Caution
Section titled “⚠️ Apply with Caution”- Change: 50-75% reduction or 100-200% increase
- Workload: Important but not critical
- Metrics: 3-7 days of data
- Savings: 20-30%
- Action: Monitor closely for 24-48 hours after applying
❌ Don’t Apply Yet
Section titled “❌ Don’t Apply Yet”- Change: More than 75% reduction or more than 200% increase
- Workload: Critical production workload
- Metrics: Less than 3 days of data
- Action: Wait for more data or adjust policy settings
Applying Recommendations
Section titled “Applying Recommendations”Manual Application (GitOps-Friendly)
Section titled “Manual Application (GitOps-Friendly)”Update your deployment YAML with recommended values:
apiVersion: apps/v1kind: Deploymentmetadata: name: my-appspec: template: spec: containers: - name: nginx resources: requests: cpu: 250m # From optipod.io/recommendation.nginx.cpu-request memory: 512Mi # From optipod.io/recommendation.nginx.memory-request limits: cpu: 500m # From optipod.io/recommendation.nginx.cpu-limit memory: 1Gi # From optipod.io/recommendation.nginx.memory-limitCommit to Git and let your GitOps tool apply the changes.
Using kubectl
Section titled “Using kubectl”kubectl set resources deployment my-app \ --requests=cpu=250m,memory=512Mi \ --limits=cpu=500m,memory=1GiAutomated Application
Section titled “Automated Application”Switch to Auto mode to let OptiPod apply recommendations automatically:
apiVersion: optipod.optipod.io/v1alpha1kind: OptimizationPolicymetadata: name: my-policyspec: mode: Auto # Changed from Recommend # ... rest of specMonitoring After Application
Section titled “Monitoring After Application”Check Pod Status
Section titled “Check Pod Status”# List podskubectl get pods -l app=my-app
# Describe pod for detailskubectl describe pod <pod-name>
# Check for restartskubectl get pods -l app=my-app \ -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.containerStatuses[*].restartCount}{"\n"}{end}'Monitor Resource Usage
Section titled “Monitor Resource Usage”# Current usagekubectl top pod -l app=my-app
# Watch usage over timewatch -n 5 'kubectl top pod -l app=my-app'Watch for Issues
Section titled “Watch for Issues”# Check for OOM killskubectl get events --field-selector reason=OOMKilled --sort-by='.lastTimestamp'
# Check for CPU throttling (if metrics available)kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/default/pods | \ jq '.items[] | select(.metadata.labels.app=="my-app") | {name: .metadata.name, containers: .containers}'
# Check logs for errorskubectl logs -l app=my-app --tail=100 --all-containersBulk Review
Section titled “Bulk Review”Using the OptiPod Recommendation Report Script
Section titled “Using the OptiPod Recommendation Report Script”OptiPod provides a comprehensive script to generate HTML or JSON reports of all recommendations across your cluster.
Download and run the script:
# Download the scriptcurl -fsSL https://raw.githubusercontent.com/Sagart-cactus/optipod/main/scripts/optipod-recommendation-report.sh -o optipod-recommendation-report.shchmod +x optipod-recommendation-report.sh
# Generate HTML report./optipod-recommendation-report.sh -o html -f optipod-recommendations.html
# Open in browseropen optipod-recommendations.htmlGenerate JSON for automation:
./optipod-recommendation-report.sh -o json -f optipod-recommendations.jsonFilter by namespace:
./optipod-recommendation-report.sh -o html -f report.html --namespace productionThe report includes:
- Current vs recommended resources for all containers
- Replica-weighted impact calculations
- Warnings for potential issues
- Sortable and filterable table
- Visual summary cards

Quick Review Script (Example)
Section titled “Quick Review Script (Example)”If you need a simple command-line summary, here’s an example script:
#!/bin/bash# This is an example script - save as review-recommendations.sh
echo "Reviewing all OptiPod recommendations..."echo "========================================"echo
kubectl get deployments -A -o json | \ jq -r '.items[] | select(.metadata.annotations."optipod.io/managed" == "true") | { namespace: .metadata.namespace, name: .metadata.name, policy: .metadata.annotations."optipod.io/policy", lastRecommendation: .metadata.annotations."optipod.io/last-recommendation", containers: [ .metadata.annotations | to_entries | map(select(.key | startswith("optipod.io/recommendation.") and (.key | contains(".cpu-request")))) | map(.key | split(".")[1]) ] | flatten | unique } | "\(.namespace)/\(.name):\n Policy: \(.policy)\n Last Updated: \(.lastRecommendation)\n Containers: \(.containers | join(", "))\n"'Export Recommendations to YAML (Example Script)
Section titled “Export Recommendations to YAML (Example Script)”Here’s an example script to export recommendations in YAML format:
#!/bin/bash# Save this script and make it executable: chmod +x export-recommendations.sh
DEPLOYMENT=$1NAMESPACE=${2:-default}
if [ -z "$DEPLOYMENT" ]; then echo "Usage: $0 <deployment-name> [namespace]" exit 1fi
echo "# Recommended resources for $NAMESPACE/$DEPLOYMENT"echo "# Generated: $(date)"echo
kubectl get deployment $DEPLOYMENT -n $NAMESPACE -o json | \ jq -r ' .metadata.annotations | to_entries | map(select(.key | startswith("optipod.io/recommendation."))) | group_by(.key | split(".")[1]) | map({ container: .[0].key | split(".")[1], resources: ( map({ key: (.key | split(".")[2] | gsub("-"; "_")), value: .value }) | from_entries ) }) | map(" - name: \(.container)\n resources:\n requests:\n cpu: \(.resources.cpu_request // "N/A")\n memory: \(.resources.memory_request // "N/A")\n limits:\n cpu: \(.resources.cpu_limit // "N/A")\n memory: \(.resources.memory_limit // "N/A")")[] 'Usage:
# Save the script above as export-recommendations.shchmod +x export-recommendations.sh
# Export recommendations./export-recommendations.sh my-app production > recommended-resources.yamlBest Practices
Section titled “Best Practices”- Review regularly: Check recommendations weekly
- Start small: Apply to non-critical workloads first
- Monitor closely: Watch for 24-48 hours after applying
- Document decisions: Note why you applied or rejected recommendations
- Track results: Measure actual savings and impact
- Iterate: Adjust policy settings based on results
- Automate gradually: Start with Recommend mode, move to Auto mode after building confidence
Troubleshooting
Section titled “Troubleshooting”No Recommendations Appearing
Section titled “No Recommendations Appearing”Check:
# Verify workload is labeled correctlykubectl get deployment my-app -o jsonpath='{.metadata.labels}'
# Check if workload is managedkubectl get deployment my-app -o jsonpath='{.metadata.annotations.optipod\.io/managed}'
# Check policy statuskubectl describe optimizationpolicy <policy-name>
# Verify metrics are availablekubectl logs -n optipod-system -l app.kubernetes.io/name=optipod --tail=50Recommendations Seem Too Low/High
Section titled “Recommendations Seem Too Low/High”Investigate:
# Check policy configurationkubectl get optimizationpolicy <policy-name> -o yaml
# Review percentile settingkubectl get optimizationpolicy <policy-name> -o jsonpath='{.spec.metricsConfig.percentile}'
# Review safety factorkubectl get optimizationpolicy <policy-name> -o jsonpath='{.spec.metricsConfig.safetyFactor}'
# Check resource boundskubectl get optimizationpolicy <policy-name> -o jsonpath='{.spec.resourceBounds}'
# Check actual resource usagekubectl top pod -l app=my-appRecommendations Not Updating
Section titled “Recommendations Not Updating”Verify:
# Check last recommendation timestampkubectl get deployment my-app \ -o jsonpath='{.metadata.annotations.optipod\.io/last-recommendation}'
# Check policy modekubectl get optimizationpolicy <policy-name> -o jsonpath='{.spec.mode}'
# Check reconciliation intervalkubectl get optimizationpolicy <policy-name> -o jsonpath='{.spec.reconciliationInterval}'
# Review operator logskubectl logs -n optipod-system -l app.kubernetes.io/name=optipod --tail=100