Troubleshooting GKE Cluster Autoscaler Scale-Up Blockers: A Complete Guide

The GKE Cluster Autoscaler fails to scale up when pending Pods cannot be matched to available node capacity due to resource constraints, quota limits, ComputeClass mismatches, or stale reservation caches.

The GKE Cluster Autoscaler (CA) is a core Kubernetes component that continuously monitors pending Pods and evaluates whether to expand existing node pools or provision new ones through Node Auto-Provisioning (NAP). When scale-up doesn't occur as expected, the CA emits specific messageId events that pinpoint the root cause. This guide walks through systematic troubleshooting based on the google/skills repository's authoritative debugging resources.

Understanding the GKE Cluster Autoscaler Architecture

Before diving into troubleshooting, it's essential to understand how the CA interacts with related components according to the source analysis.

Key Components Involved in Scale-Up Decisions

Component Role in Scale-Up
Cluster Autoscaler Reads pending Pods, evaluates ComputeClass limits, quota, reservations, and node-pool settings
Node Auto-Provisioning (NAP) Creates brand-new node pools on-the-fly when existing pools cannot satisfy requests
ComputeClass Declarative resource defining allowed machine families, zones, and optional reservation blocks
Reservations Pre-allocated capacity CA can target; subject to 30-minute cache staleness
Quotas & Committed Use Discounts (CUDs) Project-level limits CA respects automatically
Pod-Level Selectors Labels like cloud.google.com/machine-family and gke-spot that must match ComputeClass configuration

Common GKE Cluster Autoscaler Scale-Up Blockers

The CA emits diagnostic messageId values that directly explain why a scale-up request was rejected. Here are the most frequent blockers found in GKE production clusters.

Resource and Infrastructure Blockers

messageId Root Cause Resolution
scale.up.error.out.of.resources GCE stock-out in requested zone/machine family Add fallback zones/families in ComputeClass configuration
scale.up.error.quota.exceeded Project quota limit reached Request higher regional quota in Google Cloud Console
scale.up.error.ip.space.exhausted Subnet IP address pool exhausted Expand the Pod IP CIDR range

Configuration and Policy Blockers

messageId Root Cause Resolution
scale.up.no.scale.up No ComputeClass priority matches Pod resource requests Verify Pod resource requests and selectors align with ComputeClass
scale.up.error.reservation.stale CA cache hasn't synced new reservation (created < 30 min ago) Wait ≥30 minutes after reservation creation before relying on it

The 30-minute reservation cache lag is a particularly subtle blocker documented in skills/cloud/gke-cluster-autoscaler/references/ca-optimization.md. The CA maintains an internal cache of available reservations that refreshes periodically; newly created reservations are invisible to scale-up decisions until this cache synchronizes.

Step-by-Step Debugging Workflow

Follow this systematic approach to diagnose GKE Cluster Autoscaler scale-up failures using scripts and commands from the google/skills repository.

Step 1: Stream Live Autoscaler Events

The repository provides assets/log-autoscaler-events.sh for real-time visibility into CA decisions:

./assets/log-autoscaler-events.sh <cluster-name>

This script tails the Cluster Autoscaler's visibility logs, exposing noDecisionStatus.noScaleUp fields that contain the exact messageId and rejection reason.

Step 2: Inspect the Pending Pod

Examine why Kubernetes cannot schedule the Pod:

kubectl describe pod <pod-name>

Look for events indicating:

  • Insufficient cpu
  • Insufficient memory
  • node(s) had taints that the pod didn't tolerate
  • node(s) didn't match Pod's node affinity/selector

Step 3: Run the Pending Pod Checklist

From skills/cloud/gke-cluster-autoscaler/references/ca-debug.md, verify these common misconfigurations:

  • --max-nodes limits are not hit on target node pools
  • Selector conflicts are absent (e.g., gke-spot=true label on a Pod targeting an On-Demand ComputeClass)
  • Node pool auto-creation is enabled: nodePoolAutoCreation.enabled: true
  • The exact noDecisionStatus.noScaleUp reason is identified in visibility logs

Step 4: Check for Reservation Cache Lag

If your workload depends on a recently created reservation:


# Verify reservation creation timestamp

gcloud compute reservations describe <reservation-name> --zone=<zone> \
  --format='table(name,creationTimestamp)'

If created within ~30 minutes, delay scale-up testing until the CA cache refreshes. This behavior is explicitly documented in the CA optimization reference.

Step 5: Identify Scale-Down Blockers (If CA Appears Stuck)

Run the helper script to detect why nodes might be retained:

./assets/find-scale-down-blockers.sh

Common scale-down blockers include:

  • Bare Pods (not managed by a controller)
  • Local storage (emptyDir on SSD, persistent local volumes)
  • cluster-autoscaler.kubernetes.io/safe-to-evict: "false" annotations
  • PodDisruptionBudgets with maxUnavailable: 0 or minAvailable equal to replica count
  • Non-zero min-nodes floor on the node pool

Step 6: Performance Tuning for Sluggish Scale-Up

If CA responds slowly but eventually scales:


# Reduce node pool count ( >200 pools degrades CA performance)

# Prefer 'preferred' anti-affinity over 'required' anti-affinity

# Use topologySpreadConstraints instead of hard anti-affinity where possible

# Increase Spot node graceful shutdown period

kubectl patch nodeconfig default -n gke-managed-system \
  --type=merge -p '{"spec":{"shutdownGracePeriodSeconds":120}}'

Practical Code Reference

Essential Diagnostic Commands


# Stream live autoscaler events

assets/log-autoscaler-events.sh my-gke-cluster

# Show why a Pod cannot be scheduled

kubectl describe pod my-app-5678

# Look for: "Insufficient cpu", "node(s) had taints", etc.

# Verify node pool limits aren't blocking scale-up

kubectl get nodepool my-pool -o yaml | grep -A2 maxNodes

# Enable node pool auto-creation if disabled

kubectl patch nodepool my-pool \
  --type=merge -p '{"spec":{"nodePoolAutoCreation":{"enabled":true}}}'

# Segregate system Pods to dedicated pool

kubectl label ns kube-system \
  cloud.google.com/default-compute-class-non-daemonset=system-pool

# Manually clean up stale VMs when CA is stuck at min-nodes = 0

gcloud compute instances delete <stale-instance> --zone=<zone>

Key Source Files in google/skills

File Path Purpose
skills/cloud/gke-cluster-autoscaler/references/ca-debug.md Complete messageId cheat-sheet, pending Pod checklist, live visibility scripts
skills/cloud/gke-cluster-autoscaler/references/ca-optimization.md Capacity buffer tuning, reservation cache lag documentation, optimization profiles
skills/cloud/gke-cluster-autoscaler/references/ca-provisioning.md ComputeClass provisioning, fallback policies, NAP configuration
skills/cloud/gke-cluster-autoscaler/SKILL.md High-level Autoscaler overview, terminology, best practices
skills/cloud/cloud-logging-query-generation/references/query_gke.md Pre-built Cloud Logging queries for Autoscaler event analysis
assets/log-autoscaler-events.sh Real-time CA log streaming script
assets/find-scale-down-blockers.sh Automated scan for scale-down blocking conditions

Summary

  • Decode messageId values from CA visibility logs to immediately identify scale-up blockers: resource exhaustion, quota limits, IP exhaustion, or ComputeClass mismatches
  • Account for 30-minute reservation cache lag when testing newly created reservations for scale-up
  • Use assets/log-autoscaler-events.sh for live debugging and assets/find-scale-down-blockers.sh for cleanup analysis
  • Verify Pod selectors align with ComputeClass definitions, especially for gke-spot and machine-family constraints
  • Reference ca-debug.md and ca-optimization.md from the google/skills repository for authoritative troubleshooting procedures

Frequently Asked Questions

How do I know if the Cluster Autoscaler is even running?

Check for the cluster-autoscaler deployment in the kube-system namespace:

kubectl get deployment cluster-autoscaler -n kube-system

If present and running, examine its logs with kubectl logs -n kube-system deployment/cluster-autoscaler. The assets/log-autoscaler-events.sh script provides a higher-level view specifically designed for scale-up visibility.

Why does my reservation not trigger scale-up immediately?

The GKE Cluster Autoscaler maintains a 30-minute internal cache for reservation availability as implemented in the CA's capacity planning layer. Reservations created more recently are not visible to scale-up decisions until this cache refreshes. Wait at least 30 minutes after reservation creation before expecting CA to target it, or check ca-optimization.md for cache refresh behavior details.

What is the difference between scale-up and Node Auto-Provisioning?

Scale-up expands an existing node pool by adding nodes. Node Auto-Provisioning (NAP) creates an entirely new node pool when no existing pool matches the Pod's requirements. NAP requires nodePoolAutoCreation.enabled: true in the node pool or cluster configuration. NAP decisions appear in visibility logs with distinct provisioning events.

How can I tell if quota is blocking my scale-up?

Search CA visibility logs for scale.up.error.quota.exceeded. The specific quota type (CPUs, in-use addresses, SSD total capacity) appears in the event details. Alternatively, run:

gcloud compute project-info describe --project <project-id>

to inspect current quota usage against limits, then request increases in the Google Cloud Console IAM & Admin > Quotas section.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →