DEV Community

Cover image for Cluster Autoscaler Won't Remove a Node and Why I Check PDBs, Storage and Scheduling Constraints
Rakesh Tanwar
Rakesh Tanwar

Posted on

Cluster Autoscaler Won't Remove a Node and Why I Check PDBs, Storage and Scheduling Constraints

When a Kubernetes cluster scales out correctly but refuses to scale back in, people often blame Cluster Autoscaler.

Sometimes the autoscaler is doing exactly what I want.

It has found a node that appears underused, attempted to determine whether its Pods can move elsewhere, and concluded that removing the node would violate a workload constraint.

That distinction matters.

I never troubleshoot failed scale-down by looking only at CPU utilization.

An empty-looking node may not be removable

Node autoscaling is fundamentally a scheduling problem.

Before a node can disappear, workloads running there usually need somewhere safe to go.

That means the autoscaler must consider much more than available CPU and memory.

A Pod may depend on a disruption budget, local storage, node affinity, zone-specific storage, taints, topology rules, or another scheduling condition.

The node can therefore look wasteful on a dashboard while remaining necessary to the scheduler.

I check PodDisruptionBudgets first

PodDisruptionBudgets are one of my first checks.

A PDB can intentionally prevent voluntary eviction when removing a Pod would take the workload below its availability requirement.

That is useful.

The problem appears when the PDB and replica count make voluntary disruption mathematically impossible.

A three replica application with a sensible budget may drain easily.

A single replica application requiring that single replica to remain available cannot.

I inspect the current healthy count, desired healthy count, and allowed disruptions.

I also look for old PDBs whose selectors no longer match the intended workload architecture.

The right fix is rarely to delete the PDB blindly.

I usually fix the availability model.

Local storage changes the equation

Local ephemeral data can also make node removal difficult.

Whenever applications depend on node-local state, I ask what happens when the Pod moves.

Production workloads that require durable data generally belong on storage designed for that lifecycle rather than relying on a specific worker.

Stateful applications deserve particular attention because a volume may also have topology restrictions.

AceCloud's guide to Kubernetes CSI volume snapshots is a useful reminder that storage lifecycle, restore behavior, and application consistency must be designed independently from node lifecycle.

Scheduling rules can create invisible anchors

Next I inspect hard node selectors and node affinity.

A Pod might be allowed on only two nodes in the entire cluster.

If one is being evaluated for removal and the other lacks capacity, scale-down stops.

Taints and tolerations can create similar restrictions.

Topology spread rules and anti-affinity can also require a particular distribution across zones, nodes, or failure domains.

These are often good availability controls.

The problem is not that they exist.

The problem is when teams create them without realizing they also constrain autoscaling.

I compare requests with real usage

Cluster Autoscaler generally reasons about schedulability using requested resources rather than the utilization percentage displayed on a monitoring dashboard.

This difference catches many teams.

A node may show low real CPU usage while the Pods on it request most of the node's allocatable CPU.

From the scheduler's perspective, those requests matter.

I therefore look for over-requested workloads.

Right-sizing requests can make scale-down easier without sacrificing application reliability.

This is also why I like treating node autoscaling as part of the platform rather than as a separate cost script. Managed services such as AceCloud Kubernetes combine node groups and autoscaling with the wider cluster lifecycle, but workload requests and scheduling policy still determine how effectively capacity can be reclaimed.

DaemonSets deserve a separate look

DaemonSet Pods are expected to exist on relevant nodes.

Monitoring agents, security agents, log collectors, CNI components, and GPU components often use DaemonSets.

I distinguish these from normal application Pods during troubleshooting because their scale-down treatment differs.

The important question is whether an ordinary workload is anchoring the node.

I inspect autoscaler events before changing anything

Autoscaler logs usually tell a better story than utilization graphs.

I look for messages showing which Pod prevented removal and why.

That evidence turns an apparently mysterious scale-down failure into a scheduling investigation.

Only after finding the blocker do I change PDBs, requests, affinity, or storage placement.

I design workloads to be removable

My preferred approach is preventive.

For workloads expected to run on elastic worker pools, I ask whether the Pod can survive being moved at any reasonable time.

If the answer is no, I either redesign the workload or place it on a more stable node group.

Autoscaling works best when applications cooperate with infrastructure elasticity.

A node that cannot be drained is not truly elastic capacity.

That is the principle I keep in mind whenever Cluster Autoscaler refuses to remove a machine.

Before blaming the autoscaler, I ask which workload rule is telling Kubernetes that the node still matters.

Top comments (0)