← Back to Guide
Lifecycle Management L1 · INTRO ~30 min

Scan for Deprecated APIs with Pluto

Run Pluto against a cluster and manifest repository to identify deprecated and removed API versions. Produce a remediation list for upgrading from Kubernetes 1.27 to 1.29, and update the manifests to use current API groups.

Objective

Kubernetes removes deprecated API versions with minor version upgrades. Applying manifests with removed APIs to an upgraded cluster results in errors. Pluto detects these proactively — before the upgrade — so you can fix them first. This exercise scans both live cluster resources and local manifest files, and walks through the corrections needed for a 1.27 → 1.29 upgrade.

Prerequisites

Common API Deprecations: 1.27 → 1.29

ResourceDeprecated APICurrent APIRemoved In
HorizontalPodAutoscalerautoscaling/v2beta2autoscaling/v21.26
CronJobbatch/v1beta1batch/v11.25
PodDisruptionBudgetpolicy/v1beta1policy/v11.25
PodSecurityPolicypolicy/v1beta1(removed — use PSS)1.25
EndpointSlicediscovery.k8s.io/v1beta1discovery.k8s.io/v11.25
RuntimeClassnode.k8s.io/v1beta1node.k8s.io/v11.25

Steps

01

Create sample deprecated manifests for testing

# Create a directory with deprecated API manifests
mkdir deprecated-manifests

# bad-hpa.yaml — uses deprecated autoscaling/v2beta2
cat > deprecated-manifests/bad-hpa.yaml << 'EOF'
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: my-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
EOF

# bad-pdb.yaml — uses deprecated policy/v1beta1
cat > deprecated-manifests/bad-pdb.yaml << 'EOF'
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: my-pdb
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: my-app
EOF

# bad-cronjob.yaml — uses deprecated batch/v1beta1
cat > deprecated-manifests/bad-cronjob.yaml << 'EOF'
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: my-cronjob
spec:
  schedule: "0 0 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: job
            image: busybox
          restartPolicy: OnFailure
EOF
02

Scan manifest files with Pluto

# Scan all YAML files in the directory
pluto detect-files \
  -d deprecated-manifests/ \
  --target-versions k8s=v1.29.0

# Expected output:
# NAME        KIND                      VERSION              REPLACEMENT         REMOVED   DEPRECATED
# my-hpa      HorizontalPodAutoscaler   autoscaling/v2beta2  autoscaling/v2      false     true
# my-pdb      PodDisruptionBudget       policy/v1beta1       policy/v1           true      true
# my-cronjob  CronJob                   batch/v1beta1        batch/v1            true      true

# Get detailed output in wide format
pluto detect-files \
  -d deprecated-manifests/ \
  --target-versions k8s=v1.29.0 \
  -o wide

# Output as JSON for pipeline integration
pluto detect-files \
  -d deprecated-manifests/ \
  --target-versions k8s=v1.29.0 \
  -o json | jq '.items[] | {name:.Name, removed:.Removed, replacement:.Replacement}'
03

Scan live cluster resources

Scanning the cluster catches resources that were deployed with old manifests and are still running with deprecated API versions tracked in etcd.

# Apply the deprecated manifests to test scanning in-cluster
kubectl apply -f deprecated-manifests/
# Note: Some will succeed (deprecated but not removed in your K8s version)
# Some may fail if you're on 1.25+ and the API is removed

# Scan all cluster resources for deprecated APIs
pluto detect-all-in-cluster \
  --target-versions k8s=v1.29.0

# Scan specific Helm releases
pluto detect-helm \
  --target-versions k8s=v1.29.0

# Output: shows which Helm releases contain deprecated resources
# RELEASE     NAMESPACE   NAME     KIND    VERSION       REMOVED   DEPRECATED

# Count issues by severity
pluto detect-all-in-cluster \
  --target-versions k8s=v1.29.0 \
  -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
removed = [i for i in data.get('items',[]) if i.get('Removed')]
deprecated = [i for i in data.get('items',[]) if i.get('Deprecated') and not i.get('Removed')]
print(f'Removed APIs (blocking): {len(removed)}')
print(f'Deprecated APIs (warning): {len(deprecated)}')
"
04

Fix the deprecated manifests

# Fixed HPA — autoscaling/v2beta2 → autoscaling/v2
cat > deprecated-manifests/bad-hpa.yaml << 'EOF'
apiVersion: autoscaling/v2    # FIXED: was autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: my-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
EOF

# Fixed PDB — policy/v1beta1 → policy/v1
cat > deprecated-manifests/bad-pdb.yaml << 'EOF'
apiVersion: policy/v1         # FIXED: was policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: my-pdb
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: my-app
EOF

# Fixed CronJob — batch/v1beta1 → batch/v1
cat > deprecated-manifests/bad-cronjob.yaml << 'EOF'
apiVersion: batch/v1          # FIXED: was batch/v1beta1
kind: CronJob
metadata:
  name: my-cronjob
spec:
  schedule: "0 0 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: job
            image: busybox
          restartPolicy: OnFailure
EOF
05

Validate the fixes with Pluto

# Re-scan — should show no deprecated APIs
pluto detect-files \
  -d deprecated-manifests/ \
  --target-versions k8s=v1.29.0

# Expected: "There were no resources found with known deprecations."

# Verify manifests are valid Kubernetes YAML
kubectl apply -f deprecated-manifests/ --dry-run=client
# Expected: no errors (dry-run validates against current API server)

# Add Pluto to your CI pipeline to prevent future regressions
# GitHub Actions step example:
# - name: Pluto API deprecation check
#   run: |
#     pluto detect-files -d k8s/ --target-versions k8s=v1.29.0
#     pluto detect-helm --target-versions k8s=v1.29.0

Success Criteria

Further Reading