Descheduler in Kubernetes

Descheduler in Kubernetes

The Kubernetes scheduler picks a home for a Pod once and never looks back. The Descheduler is the helper that notices when that choice has gone stale, politely asks the Pod to leave, and lets the scheduler choose again. This guide explains how it works, when to use it and how to use it safely.

Picture a wedding where guests are shown to their tables when they arrive. It is a good plan at the start. Then some guests leave early, a new table is added at the back, two people who should not sit together end up side by side, and one table is crowded while another is half empty. The seating manager has finished the job and walks away, so nothing is ever corrected. Kubernetes has exactly this problem with Pods, and the Descheduler is the person who walks around the room at intervals and gently asks a few guests to stand up so that they can be seated again, this time in a better place.

1What the Descheduler Is and Why It Exists

A short definition first, so every later chapter has something solid to stand on.

Kubernetes runs your applications inside Pods, and Pods run on nodes, which are the computers of the cluster. A component called the scheduler decides which node each new Pod should go to. The Descheduler is a separate, optional program that looks at Pods that are already running and finds the ones whose current placement is no longer a good fit. It then removes those Pods using the safe eviction process, and the normal controller of each Pod, such as a Deployment, creates a fresh replacement. The scheduler places that replacement, and with luck it lands somewhere better.

The key idea is in the name. To schedule is to choose a place. To deschedule is to take a Pod out of its place so that it can be scheduled again. The Descheduler never chooses a node itself. It only decides who should leave.

Simple Analogy

Think of a school bus driver who drops children at stops (the scheduler) and a teacher who later notices that three children live next door to each other but were left at different stops. The teacher cannot drive the bus. She can only ask the children to get off and wait for the bus to pick them up again. That teacher is the Descheduler.

Term 1

Pod

The smallest unit that Kubernetes runs. It holds one or more containers that share a network address and storage.

Term 2

Node

A machine, real or virtual, that provides CPU, memory and storage for Pods to use.

Term 3

Scheduler

The control plane component that picks a node for each Pod that does not have one yet.

Term 4

Eviction

The polite way of removing a Pod. It respects safety rules and gives the Pod time to shut down properly.

Term 5

Descheduler

An add-on program that evicts badly placed Pods so that the scheduler can place them again.

i
Remember

The Descheduler is not part of the core of Kubernetes. It is a separate project from the Kubernetes community that you install on top of a cluster when you need it.

2A Quick Look at How Scheduling Works

To understand what goes stale, we first need to see how a placement decision is made.

When you create a Pod, it first has no node. The scheduler notices it and runs two big steps. The first step is filtering. The scheduler throws out every node that cannot run the Pod, for example because it lacks enough free CPU or memory, because it has a taint that the Pod does not tolerate, or because a rule in the Pod says it must run on a certain kind of machine. The second step is scoring. Among the nodes that remain, the scheduler gives each one a score based on things like how much room is left and whether the Pod would be nicely spread out, and it chooses the highest score.

Once the choice is made, the scheduler writes the node name into the Pod, and the job is finished. The Pod runs there until it ends, crashes or is removed. The scheduler does not wake up later to ask whether the choice still looks right.

1

A new Pod appears

A controller or a person creates a Pod. It has no node yet, so it waits in the scheduler’s queue.

2

Filter

Nodes that cannot host the Pod because of capacity, taints or rules are removed from the list.

3

Score

The remaining nodes are ranked, and the best one is picked.

4

Bind and forget

The Pod is bound to the chosen node. From now on the scheduler considers the work complete.

“The scheduler decides once, at birth. Nothing in core Kubernetes ever asks whether that decision still makes sense.”

This design is deliberate and good. It keeps the scheduler fast and predictable, and it avoids a world where running applications are constantly shuffled around. But it also means that placement can grow old, and that is the gap the Descheduler fills.

!
Common Misconception

Many people believe Kubernetes automatically rebalances Pods when a new node joins. It does not. A new empty node only receives Pods that are created after it joins.

3Why Placement Goes Stale: Cluster Drift

A choice that was perfect at 9 in the morning can be a poor choice by lunchtime.

The state of a cluster keeps changing. A placement that satisfied every rule when the Pod was created may no longer match the cluster today. The difference between the ideal placement and the real placement is often called drift. There are several common reasons for it.

New nodes join the cluster

When an autoscaler adds nodes during a busy period, new Pods use them. When the busy period ends and some Pods go away, the old nodes may stay full while the new nodes stay nearly empty.

Nodes are removed and come back

After a node upgrade, a crash or the loss of a spot machine, Pods move to whichever nodes have room at that moment. When the node returns, the Pods do not move back, and the load stays uneven.

Labels and taints change

An administrator may add a taint to a node to reserve it, or change a label that a Pod’s rule depends on. Pods that were allowed when they arrived may now break the rule, yet they keep running.

Rules are checked only at scheduling time

Many Pod placement rules, such as node affinity, are described as “ignored during execution”. This means they are enforced when the Pod is placed and never again while it runs.

Usage changes over time

A Pod placed on a quiet node may grow hungrier as traffic increases. The node can become crowded without the scheduler ever hearing about it.

Pods fail or restart again and again

Some Pods get stuck in a pattern of repeated restarts or end up in a failed state, and quietly occupy space on a node.

Simple Analogy

A tidy bookshelf slowly becomes messy. Nobody does anything wrong on any single day, but books are added, borrowed and returned in whatever gap is free. After a year, one shelf is overflowing and another is empty. A regular tidy-up fixes it, and the Descheduler is the tidy-up for your cluster.

What changedResulting problemDescheduler idea
New empty nodes addedOld nodes stay overloadedMove some Pods to the new nodes
Taint added to a nodePods that do not tolerate it still run thereEvict Pods that violate the taint
Node label changedPods no longer match their affinity ruleEvict Pods that violate the affinity
Many replicas on one nodeOne node failure takes down many copiesEvict duplicates so they spread
Pod keeps crashingWasted resources and noisy alertsEvict Pods with too many restarts

4The Big Picture: Where the Descheduler Fits

Three actors share this job: the Descheduler decides who leaves, a controller makes a replacement, and the scheduler picks the new home.

The easiest way to hold the picture in your head is as a loop. The Descheduler wakes up, reads the state of the cluster from the API server, decides that a particular Pod is poorly placed, and asks the API server to evict it. The Pod’s owner, such as a Deployment, sees that a Pod has disappeared and creates a new one. The scheduler then sees an unplaced Pod and picks a node for it using the rules of the day.

flowchart LR
  D["Descheduler"] -->|"1 Read nodes and Pods"| A["API Server"]
  D -->|"2 Evict a badly placed Pod"| A
  A -->|"3 Pod removed"| C["Owner controller, such as a Deployment"]
  C -->|"4 Create replacement Pod"| A
  A -->|"5 Unplaced Pod appears"| S["Scheduler"]
  S -->|"6 Bind Pod to a better node"| A
        
FIG 1 — The descheduling loop and the other components it relies on

This division of labor is important for two reasons. First, the Descheduler needs no special power to place Pods, so it cannot place them anywhere that the scheduler would not. Second, the replacement Pod passes through all the normal checks again, including quotas and admission rules, so nothing is bypassed.

0
Pods placed by the Descheduler itself
1
Safe API used for removal: eviction
10
Built-in plugins covering common problems
!
Important

A Pod that has no owner, such as one created by hand, will not come back after eviction, because nothing exists to create a replacement. This is one reason the Descheduler is careful with such Pods by default.

5Ways to Run the Descheduler

The Descheduler is itself an ordinary program running in your cluster, and you choose how often it runs.

The Descheduler is packaged as a container image and is commonly installed with a Helm chart or with the manifests that its project provides. It normally lives in the kube-system namespace and is given a high priority class so that it is not itself removed when the cluster is under pressure. There are three ways to run it.

Mode 1

Job

Runs one time and stops. Useful for a manual clean-up, for testing a new policy, or for being triggered by your own automation.

Mode 2

CronJob

Starts on a schedule, does one pass and exits. A good default because each run starts fresh and the schedule is easy to read.

Mode 3

Deployment

Runs all the time and repeats its work at a chosen interval. Useful when you want leader election and continuous behavior.

Advantages of scheduled runs

  • Simple to understand and to pause
  • Each run starts with a clean state
  • Easy to align with quiet hours
  • Failed runs are visible as failed Jobs

Disadvantages / Trade-offs

  • Drift stays until the next scheduled run
  • A Deployment reacts more steadily but runs all day
  • Too frequent runs can cause constant churn

The service account and its permissions

Because the Descheduler talks to the API server, it needs an identity and permissions. It runs under a dedicated service account that is allowed to read nodes, Pods, namespaces and priority classes, and to create evictions. It does not need permission to create Pods or to change nodes. Keeping its rights this narrow means that even a bug in the program cannot do more harm than evicting Pods.

i
Good Practice

Start with a one-time Job in dry-run mode on a test cluster. Move to a CronJob once you trust the results, and only then think about running it more often.

6The Policy: Profiles, Plugins and Extension Points

All the Descheduler’s behavior is controlled by one policy that you write, and nothing happens that the policy does not ask for.

The Descheduler does nothing on its own initiative beyond reading its policy. The policy is a small configuration object that is stored in a ConfigMap and mounted into the Descheduler Pod. In the current style, the policy is organized into profiles. A profile is a named group of settings that says which plugins to turn on and how each one should behave.

Types of plugin

The behavior of the Descheduler is split into plugins. Plugins fall into a few types, which the project calls extension points.

Plugin typeJobExamples
DescheduleLooks at Pods one at a time and evicts those that break a ruleNode taints, node affinity, too many restarts, Pod lifetime
BalanceLooks at groups of Pods or nodes and evicts some to even things outLow node utilization, duplicates, topology spread
FilterDecides whether a Pod is allowed to be evicted at allThe default evictor
Pre-eviction filterA last check just before a chosen Pod is evictedChecks such as whether another node could host the Pod
Simple Analogy

Imagine a restaurant manager with a checklist. Some items are about single tables, such as “this table is sitting in the wrong section”. Others are about the whole room, such as “the left side is much busier than the right”. A final rule says who may never be asked to move, like a guest in a wheelchair. Deschedule plugins, Balance plugins and the filter are those three kinds of items.

What a profile contains

A profile lists each plugin it wants, along with settings for each. It also configures the default evictor, which is the safety filter described in the next chapters. Several profiles can exist side by side, for example one for your general workloads and one for a special group of nodes. Within a pass, all Deschedule plugins run before Balance plugins, and the filters are consulted before any Pod is actually evicted.

i
Good Practice

Turn on only the plugins you need. A short, well understood policy is far easier to reason about than a long one copied from an example.

7Inside One Descheduling Run

Each run follows the same sequence of steps, from reading the cluster to counting what was evicted.

Understanding a single run helps explain why the Descheduler sometimes evicts nothing even when a cluster looks untidy. At each step, rules narrow down the candidates.

1

Load the policy

The Descheduler reads its policy and builds the profiles with their plugins and settings.

2

List the nodes

It reads all ready nodes, and if a node selector is set, keeps only the nodes that match.

3

Run the Deschedule plugins

Each enabled plugin of this type goes through the Pods and nominates the ones that break its rule.

4

Run the Balance plugins

Each enabled plugin of this type studies groups of Pods or nodes and nominates Pods that would improve balance if moved.

5

Apply filters and limits

Each nominated Pod is checked against the safety filter and the global limits on how many Pods may be evicted.

6

Evict through the API

For Pods that pass, the Descheduler sends an eviction request. The API server accepts or refuses it based on disruption budgets.

sequenceDiagram
  participant D as Descheduler
  participant A as API Server
  participant C as Deployment Controller
  participant S as Scheduler
  D->>A: List ready nodes and Pods
  A-->>D: Current cluster state
  D->>D: Plugins nominate Pods, filters remove unsafe ones
  D->>A: Create eviction for chosen Pod
  A->>A: Check disruption budget
  A-->>D: Accepted
  A-->>C: Pod deleted event
  C->>A: Create replacement Pod
  A-->>S: New unplaced Pod
  S->>A: Bind to a node
        
FIG 2 — One eviction from the Descheduler’s point of view

At the end of each run the Descheduler reports what it did through its logs and metrics. In Deployment mode, it then sleeps until the next interval. In Job and CronJob modes, it simply exits.

8Eviction Safety: The Default Evictor

A tool that removes running Pods must be very careful, and the Descheduler has several layers of protection.

The Descheduler uses the Eviction API rather than simply deleting Pods. An eviction is a polite request. The API server checks any PodDisruptionBudget that covers the Pod. A PodDisruptionBudget is a rule that says “at least this many copies of my application must stay available”. If removing the Pod would break that promise, the eviction is refused, and the Pod stays. Accepted evictions also respect the Pod’s grace period, so the application has time to finish its requests and shut down cleanly.

What is protected by default

Before the Eviction API is ever called, the Descheduler’s default evictor already filters out Pods that are risky to move. By default these are not evicted.

Protected 1

System-critical Pods

Pods with a very high priority, such as core cluster components, are left alone.

Protected 2

DaemonSet Pods

These exist to run on every node, so moving them would make no sense.

Protected 3

Pods with local storage

Data in a local emptyDir or host path would be lost on eviction, so these are skipped unless you say otherwise.

Protected 4

Pods without an owner

A Pod created by hand would not be recreated, so it is not touched by default.

Protected 5

Static Pods

These are managed directly by the kubelet on a node and cannot be rescheduled.

Protected 6

Pods under a priority threshold

You can set a priority level above which Pods are never evicted, protecting your most important work.

Tuning the evictor

The default evictor has settings you can change. You can allow eviction of Pods with local storage or of failed bare Pods, you can require that another node could actually host the Pod before it is evicted, which is called the node-fit check, you can limit eviction to Pods with a certain label, and you can protect young Pods by setting a minimum age. You can also mark a single Pod with an annotation to say that it should or should not be evicted, which gives application owners a simple way to speak up.

i
Good Practice

Always turn on the node-fit check. It prevents the Descheduler from evicting a Pod when no other node could take it, which would only leave the Pod stuck in Pending with nowhere to go.

!
Common Mistake

Applications that have only one replica and no disruption budget will be down for a short time every time they are evicted. Run at least two replicas of anything you do not want to interrupt.

9The LowNodeUtilization Plugin

This plugin spreads load by moving Pods from crowded nodes toward empty ones.

This is the plugin that most people think of first. It looks at how much of each node’s capacity is already promised to Pods, and it tries to even things out. It uses two sets of limits that you provide, one called thresholds and one called target thresholds, each given as percentages for resources such as CPU, memory and the number of Pods.

How a node is classified

A node whose use is below all of the thresholds is called underutilized. It has plenty of room. A node whose use is above any of the target thresholds is called overutilized. It is crowded. Nodes in between are considered fine and are left alone.

flowchart TD
  N["Each node"] --> Q1{"Below all thresholds?"}
  Q1 -->|Yes| U["Underutilized: can receive Pods"]
  Q1 -->|No| Q2{"Above any target threshold?"}
  Q2 -->|Yes| O["Overutilized: Pods may be evicted"]
  Q2 -->|No| OK["Balanced: left alone"]
        
FIG 3 — How LowNodeUtilization sorts nodes into three groups

How it chooses what to evict

The plugin only acts if there is at least one underutilized node and at least one overutilized node. It then evicts Pods from the overutilized nodes until they fall down toward the target level, or until the underutilized nodes have no more room to receive Pods. Among the Pods on an overutilized node, it prefers to evict less important ones first, ordering by priority and then by quality of service class, so that best-effort Pods go before guaranteed ones.

Simple Analogy

Imagine supermarket checkout lanes. Lane 1 has a queue to the door, while lanes 4 and 5 are empty. A manager asks a few people in lane 1 to move across, starting with those who have the smallest baskets. When lane 1 is shorter, or the empty lanes fill up, the manager stops.

!
Important Detail

By default this plugin looks at the resources that Pods have requested, not the CPU and memory they are actually using right now. A node can look full on paper and still be quiet in reality. Newer versions can also use live usage metrics, but you need to set this up deliberately.

Useful settings

You can require that a minimum number of underutilized nodes exist before the plugin does anything, which avoids pointless moves in small clusters. You can also choose to measure thresholds as a distance from the average usage of the cluster instead of as fixed numbers, which adapts better when the cluster as a whole is very busy or very quiet.

10The HighNodeUtilization Plugin and Compaction

Sometimes you want the opposite of spreading: you want to pack Pods tightly so that spare nodes can be removed.

Spreading Pods out is good for resilience, but it costs money in a cloud, because you pay for every node even if each one is half empty. If your goal is to save money, you want Pods packed onto as few nodes as possible, so that the empty ones can be shut down by a cluster autoscaler. This is called compaction or bin packing.

The HighNodeUtilization plugin does this. It finds nodes that use less than a given threshold and evicts Pods from them, hoping that those Pods will be placed onto other nodes that are already busier. Over time, lightly used nodes become empty. An autoscaler, seeing an empty node, can then remove it.

Simple Analogy

A bus company runs five half-empty buses on a quiet evening. By asking passengers to move into three buses, two buses can go back to the depot and save fuel. The passengers still reach their destination, and the company pays less.

!
Common Mistake

This plugin only works well when the scheduler is also set to prefer busy nodes. If the scheduler still spreads Pods evenly, the evicted Pods simply land on the same lightly used nodes, and nothing is gained. The two must agree on the goal.

AspectLowNodeUtilizationHighNodeUtilization
GoalEven out load across nodesPack Pods onto fewer nodes
Evicts fromOverutilized nodesUnderutilized nodes
Best forPerformance and resilienceCost saving with an autoscaler
Scheduler needsDefault spreading behaviorA packing preference such as favoring busy nodes
Can run together?No. They pull in opposite directions, so choose one for a given group of nodes.

11The RemoveDuplicates Plugin

Several copies of the same application on one node is a hidden risk that this plugin removes.

Applications usually run as several identical copies, called replicas, so that if one fails the others keep serving. This only helps if the copies are on different nodes. If three replicas sit on the same node and that node fails, all three disappear together, and your application is down despite having replicas.

This can happen after node restarts or upgrades, when many Pods land on whichever node is available first. The RemoveDuplicates plugin looks at Pods that share the same owner, such as the same ReplicaSet or StatefulSet, and checks how they are spread. If one node holds more than its fair share, it evicts the extra ones so that the scheduler can place them on other nodes.

Simple Analogy

A careful traveler never packs all copies of an important document in the same bag. If the bag is lost, every copy is lost. The plugin is like a friend who sees all three copies in one bag and says, “Put one in another bag.”

Fair share rule

If there are fewer nodes than replicas, the plugin does not demand one Pod per node. It works out a fair maximum per node and only evicts Pods above it.

Excluding some owners

You can exclude certain kinds of owners from this check, which is useful for workloads where having several Pods on one node is perfectly acceptable.

i
Better Long-Term Fix

Add topology spread constraints or anti-affinity to your Deployments so that the scheduler spreads replicas correctly from the start. Use this plugin as a safety net for older placements, not as the main plan.

12Topology Spread and Anti-Affinity Plugins

Rules about spreading are checked when a Pod is placed, and these plugins check them again later.

Kubernetes lets you describe how Pods should be spread across zones, nodes or other groups. A topology spread constraint says something like “keep the number of Pods in each zone within one of each other”. The amount of difference allowed is called the maximum skew. The scheduler respects this rule at the moment of placement. Afterwards, if Pods in one zone are deleted, the spread can become uneven again, and nothing corrects it.

RemovePodsViolatingTopologySpreadConstraint

This plugin checks whether the current spread breaks the rule, and if so, evicts enough Pods from the most crowded group to bring the difference back within the allowed skew. You can choose whether it should also handle soft constraints, which are preferences rather than strict rules.

RemovePodsViolatingInterPodAntiAffinity

Inter-pod anti-affinity says that certain Pods must not run next to each other, for example two copies of a database on one machine. The scheduler enforces it at placement time. If a conflict appears later, because a label changed or because of an unlucky sequence of events, this plugin evicts the Pods that break the rule so that they can be placed apart.

Simple Analogy

Two rival fans should not sit in the same row at a football match. The stewards check at the gate, but later two seats are swapped by mistake. A steward walking the rows spots the problem and asks one fan to move.

i
Good Practice

These plugins only help if your Pods actually carry the spread or anti-affinity rules. Without such rules in the Pod definitions, the scheduler has nothing to enforce when it places the replacements, and Pods may drift back to the same shape.

13Node Affinity and Node Taint Plugins

When the rules about a node change after a Pod arrived, these plugins make sure the Pod still belongs there.

RemovePodsViolatingNodeAffinity

A Pod can say that it must run only on nodes with a certain label, for example nodes with fast disks. This is called node affinity. The common form is written with a long name that ends in “ignored during execution”, and that ending is the catch. It means the rule is checked only at placement time. If an administrator later removes the label from the node, or the Pod’s rule is updated, the Pod keeps running on a node that no longer matches.

This plugin finds such Pods and evicts them, but only when another node exists where the rule would be satisfied. That second check prevents the plugin from evicting a Pod just to leave it homeless. You can choose whether it handles only the required form of affinity, only the preferred form, or both.

RemovePodsViolatingNodeTaints

A taint is a mark on a node that tells Pods to stay away unless they carry a matching toleration. Taints are often used to reserve nodes for special work or to prepare a node for maintenance. A taint with the “no schedule” effect blocks new Pods but does not remove Pods that are already on the node. This plugin removes the Pods that do not tolerate the taint, so the node is truly cleared for its new purpose.

Simple Analogy

A restaurant turns one room into a private party room and hangs a “reserved” sign on the door. New guests are stopped at the door, but the families already inside keep eating. The staff then politely asks them to move to the main room so the party room is ready. The taint plugin is that polite request.

i
Good Practice

If you prefer to evict at once, a taint with the “no execute” effect already makes Kubernetes evict Pods that do not tolerate it, without needing the Descheduler. The plugin is mainly useful for the softer “no schedule” kind.

14Lifetime, Restarts and Failed Pods

Three simple plugins deal with Pods that have lived too long, crash too often, or have already failed.

Plugin 1

PodLifeTime

Evicts Pods older than a maximum age. Useful when you want applications to be refreshed regularly, for example to pick up new settings or to avoid very long-running memory growth.

Plugin 2

RemovePodsHavingTooManyRestarts

Evicts Pods whose containers have restarted more than a set number of times. A fresh Pod on a different node may succeed where this one keeps failing.

Plugin 3

RemoveFailedPods

Evicts Pods that are in a failed state, optionally filtered by the reason and by how long ago they failed. This clears clutter that would otherwise stay around.

When these help

PodLifeTime is a gentle way to make sure nothing runs forever without being refreshed. It is best used with a long limit, such as days, and combined with a disruption budget so that restarts happen slowly. The restart plugin is useful for problems tied to one node, such as a faulty disk or a network issue, because moving the Pod away may cure the symptom. It does not fix a bug in the application itself, and an application that crashes everywhere will simply crash on its next node too.

Simple Analogy

If a printer in one room keeps jamming, it may help to move your document to a printer in another room. But if the document itself is broken, every printer will jam. The restart plugin helps with the first case only.

!
Common Mistake

Setting PodLifeTime to a very short time on important services creates constant restarts that look like instability. Use it deliberately and keep the limit generous.

15Limits, Rate Control and Dry Run

Because eviction causes real interruption, the Descheduler lets you cap how much it can do at once.

Even a correct policy can be too eager. Moving a hundred Pods in a few seconds would put a heavy load on the cluster and would be risky for the applications. The Descheduler therefore provides global limits.

SettingWhat it limitsWhy it helps
Max Pods evicted per nodeHow many Pods can leave one node in a runStops one node from being emptied too quickly
Max Pods evicted per namespaceHow many Pods can leave one team’s namespace in a runProtects a single team from being hit hard
Max Pods evicted in totalHow many Pods can be evicted in the whole runPuts a ceiling on the overall disruption
Node selectorWhich nodes the Descheduler considersLets you test on a small group of nodes first
Interval or scheduleHow often runs happenSpaces out disruption over time

Dry run

The Descheduler has a dry-run mode. In this mode, it goes through every step and writes in its logs which Pods it would evict, but it does not actually evict anything. This is the best way to learn what a new policy will do, because you can read the planned evictions and decide whether they make sense before any application is touched.

Simple Analogy

A fire drill with no real fire. Everyone practices the route to the exit and you see where people get stuck, but nobody is actually in danger. Dry run is a drill for your policy.

i
Good Practice

Begin with low limits, such as a handful of Pods per run, and raise them slowly as you gain confidence. It is easy to allow more later and painful to recover from allowing too much on the first day.

16Descheduler Compared with Related Tools

Several Kubernetes features move or remove Pods, and it helps to know which one does what.

ToolMain jobWhen it actsRelationship to the Descheduler
SchedulerChooses a node for a new PodWhen a Pod is createdPlaces the replacements the Descheduler asks for
DeschedulerEvicts badly placed PodsOn a schedule or intervalSubject of this guide
Cluster autoscaler or node provisionerAdds and removes nodesWhen Pods cannot fit or nodes are emptyRemoves nodes that compaction has emptied
Node drainEmpties one node for maintenanceWhen an administrator starts itUses the same eviction safety rules
Vertical Pod AutoscalerAdjusts Pod resource requestsWhen usage changesMay evict Pods too, so coordinate the two
Taint with no execute effectRemoves Pods that do not tolerate a taintImmediately when the taint appearsDoes part of what the taint plugin does
Simple Analogy

A hotel has a front desk that assigns rooms (scheduler), a housekeeping manager who asks guests to switch rooms when something is wrong (Descheduler), a facilities team that opens or closes whole floors as demand rises and falls (autoscaler), and a maintenance crew that clears one room for repairs (drain).

i
Working Together

Descheduler and autoscaler often make a good pair. The Descheduler packs Pods onto fewer nodes, and the autoscaler removes the nodes that become empty. Some modern node provisioners also perform their own consolidation, so check what your platform already does before adding a second tool with the same goal.

17A Worked Example: Balancing After a Scale-Up

Here is a story that shows every part working together.

A cluster has three nodes, named A, B and C. During a sale on an online shop, traffic rises and the autoscaler adds two more nodes, D and E. The Pods for the shop’s checkout service are created during the rush, and the scheduler places them wherever there is space. When the sale ends, many Pods are removed, but the old nodes A, B and C still carry most of the remaining Pods. Nodes D and E are almost empty. An engineer has installed the Descheduler as a CronJob with the LowNodeUtilization plugin turned on.

1

The run starts

The CronJob fires. The Descheduler reads the policy and lists the five ready nodes and their Pods.

2

Nodes are classified

Nodes D and E are below all thresholds, so they are underutilized. Node A is above a target threshold, so it is overutilized. B and C are in the comfortable middle.

3

Candidates are picked

On node A, the plugin lines up Pods, with the least important ones first. The safety filter removes a monitoring agent that belongs to a DaemonSet and a Pod with local storage.

4

Checks and limits

The node-fit check confirms that D or E could host each remaining candidate. The limit of five evictions per node stops the plugin after five Pods.

5

Evictions and replacements

The Descheduler asks for the eviction of each Pod. Disruption budgets are respected, so one Pod is refused for now. The Deployments create replacements, and the scheduler places most of them on D and E.

flowchart LR
  subgraph Before
    A1["Node A: crowded"]
    B1["Node B: medium"]
    D1["Node D: empty"]
  end
  subgraph After
    A2["Node A: comfortable"]
    B2["Node B: medium"]
    D2["Node D: comfortable"]
  end
  A1 -->|"Pods evicted and replaced"| D2
        
FIG 4 — Load moves from a crowded node to an empty one

After the next run, the cluster is more even. Because the engineer used a small limit, only a few Pods moved at a time, and the checkout service stayed available throughout. If the engineer also uses topology spread constraints in the Deployments, the new placements will be spread across zones as well.

18Making Your Applications Descheduler-Friendly

The Descheduler can only be gentle if the applications it touches are built to be moved.

Any Pod can be evicted at almost any time in Kubernetes, because of node maintenance, autoscaling, or failures. Applications that survive those events will also survive the Descheduler. So the real work of making a cluster safe for descheduling is making each workload robust.

Practice 1

Run several replicas

With at least two copies on different nodes, one copy can leave while the other keeps serving.

Practice 2

Set a disruption budget

A PodDisruptionBudget tells Kubernetes how many copies may be unavailable at once, and the Descheduler respects it.

Practice 3

Handle shutdown signals

The application should finish current work and close connections when it is told to stop, within the grace period.

Practice 4

Use readiness checks

A new Pod should receive traffic only when it is truly ready, so a moved Pod does not serve errors while starting.

Practice 5

Declare placement rules

Put spread and affinity rules in the Pod definition so the scheduler places replacements well.

Practice 6

Mark special Pods

Use annotations or priority levels to protect work that must never be moved, such as long-running batch calculations.

Stateful applications

Databases and other stateful applications need extra care. A StatefulSet Pod usually keeps its storage when it is recreated, but it may take time to reconnect, rejoin a cluster and warm up its caches. Evicting several such Pods quickly can cause trouble. A common approach is to exclude them from descheduling altogether, or to protect them with strict disruption budgets and a long minimum age.

!
Common Mistake

Treating batch jobs like web services. A job that has been running for hours will lose its progress if evicted. Mark such Pods as protected, or keep them out of the Descheduler’s scope with labels.

19Security and Permissions

A program that is allowed to evict Pods is powerful, so its access should be small and well watched.

The Descheduler needs to read information about nodes, Pods, namespaces and priority classes, and to create eviction requests. It does not need to read Secrets, create Pods or change nodes. Granting only those rights through a dedicated service account is the first protection.

ConcernRiskMitigation
Over-broad permissionsA bug or compromise could change far more than PodsUse a narrow cluster role with only the needed verbs
Wrong policyA careless rule could cause an outageReview policy changes like code, test with dry run
Policy tamperingSomeone edits the ConfigMap to evict important PodsRestrict who can edit the ConfigMap in kube-system
Image trustA malicious image runs with eviction rightsPin the image version and verify its source
No record of actionsHard to learn who evicted whatKeep audit logs and Descheduler logs together
Simple Analogy

A building manager who can ask tenants to move offices needs a master key to some doors but should never be given the key to the safe. Giving exactly the right keys keeps the building safe even if the manager makes a mistake.

i
Good Practice

Run the Descheduler in the system namespace with a high priority class, restrict write access to its policy, and alert on any sudden jump in the number of evictions.

20Monitoring, Logging and Metrics

If you cannot see what the Descheduler is doing, you cannot trust it.

Logs

The Descheduler writes logs that explain each run: which plugin ran, which nodes were considered, which Pods were nominated, which were filtered out and why, and which were evicted. The detail depends on the log level, and a higher level is helpful when you are learning or debugging. In dry-run mode, the logs are the main way to see what would have happened.

Events and metrics

Evictions also appear as events attached to the Pods, so you can see them with ordinary Kubernetes tools. The Descheduler exposes metrics in a format that common monitoring systems read. These include counts of evicted Pods, grouped by plugin, namespace and result, and measurements of how long each run and each plugin took.

Watch 1

Eviction count

A steady small number is normal. A sudden spike suggests a policy mistake or a big change in the cluster.

Watch 2

Failed evictions

Many refused evictions can mean disruption budgets are too tight or that Pods have nowhere else to go.

Watch 3

Run duration

Very long runs can mean a large cluster or an expensive policy that should be trimmed.

Watch 4

Repeat evictions of the same Pod

If the same Pod keeps coming back to the same node and being evicted, the plan and the scheduler disagree.

i
Good Practice

Show node usage and eviction counts on the same dashboard. You then see directly whether the evictions actually made the cluster more balanced, which is the only result that matters.

21Anti-Patterns to Avoid

These mistakes are common and each one has a simple cure.

ANTI-PATTERN-01 Avoid
Problem

Turning on many plugins at once with aggressive settings on the first day.

Why It’s Harmful

It is impossible to tell which rule caused which eviction, and a combined policy can create waves of restarts across the cluster.

Correct Approach

Enable one plugin at a time, test it with dry run, watch the results for a few days, and only then add the next.

ANTI-PATTERN-02 Avoid
Problem

Evicting Pods that the scheduler will place right back on the same node.

Why It’s Harmful

The Pod is restarted for nothing, and the cycle repeats every run. This is often called flapping and creates pure disruption with no benefit.

Correct Approach

Make sure the scheduler’s preferences agree with the plugin’s goal, use the node-fit check, and put spread rules into the Pod definitions.

ANTI-PATTERN-03 Avoid
Problem

Running both LowNodeUtilization and HighNodeUtilization for the same nodes.

Why It’s Harmful

One plugin spreads Pods out and the other packs them in. They undo each other and generate endless churn.

Correct Approach

Choose one goal per group of nodes. Use separate profiles with node selectors if two groups really need different goals.

ANTI-PATTERN-04 Avoid
Problem

Relying on the Descheduler for single-replica applications without disruption budgets.

Why It’s Harmful

Every eviction means a short outage, because there is no other copy to serve traffic while the new Pod starts.

Correct Approach

Run at least two replicas, add a disruption budget, or exclude the application from descheduling.

ANTI-PATTERN-05 Avoid
Problem

Running it very often, such as every minute, with no limits.

Why It’s Harmful

Constant small moves keep applications in permanent turbulence and make problems harder to diagnose.

Correct Approach

Use an interval of tens of minutes or longer, set eviction caps, and avoid peak traffic hours when you can.

22Rolling It Out Safely and Weighing the Trade-Offs

A cautious rollout turns the Descheduler from a risk into a quiet helper.

1

Prepare the workloads

Check that important applications have replicas, disruption budgets, readiness checks and spread rules.

2

Start in dry run on a test cluster

Read the logs and compare the planned evictions with what you expect.

3

Limit the scope

Use a node selector and a namespace limit to restrict the first real runs to a small slice of the cluster.

4

Watch and tune

Look at the dashboard after each run and adjust thresholds, limits and the schedule.

5

Widen gradually

Extend to more nodes and more plugins one step at a time, keeping a simple way to pause the Descheduler.

Advantages

  • Keeps load more even after the cluster changes
  • Restores spread and affinity rules that have drifted
  • Enables cost savings when paired with an autoscaler
  • Cleans up failed and constantly restarting Pods
  • Uses safe eviction with disruption budget support
  • Is fully controlled by a readable policy

Disadvantages / Trade-offs

  • Every eviction restarts a Pod and risks short interruption
  • Needs careful tuning and good workload design
  • Does not choose nodes, so the scheduler may disagree
  • Default load measurement is based on requests, not live usage
  • An extra component to install, upgrade and monitor
i
Is It Right for You?

Small, stable clusters with few node changes may not need a Descheduler at all. Clusters that scale often, use spot machines, change taints and labels, or care about cost and spread usually benefit the most.

23Troubleshooting Common Problems

Most problems fall into a few patterns, and each has an obvious first check.

SymptomLikely causeWhat to check first
Nothing is ever evictedNo node is underutilized, or every Pod is filtered as protectedRead the logs at a higher level to see why Pods were skipped
Pods return to the same nodeThe scheduler’s preferences disagree with the pluginCompare scheduler scoring with the policy goal and add spread rules
Evicted Pods stay PendingNo node has room or matches the rulesCheck capacity, taints, quotas and the node-fit setting
Evictions are refusedA disruption budget blocks themCheck the budget and the number of ready replicas
Descheduler Pod fails to startPolicy file is invalid or permissions are missingRead its logs and verify the ConfigMap and cluster role
Unexpected Pods evictedPolicy is broader than intendedReview labels, namespaces and the evictor settings
Too many restarts in one hourInterval is too short or limits are too highLengthen the interval and lower the caps

A calm way to investigate

First, turn on dry run so nothing else changes. Next, raise the log level and read one full run from start to finish. The logs usually say exactly why a Pod was or was not nominated. Then compare what the plugin expected with what the scheduler did when the replacement appeared. Most puzzling cases turn out to be a disagreement between these two.

!
Common Mistake

Blaming the Descheduler for every Pod restart. Pods are also restarted by node problems, autoscaling, upgrades and application crashes. Check the eviction events and metrics to confirm that the Descheduler was really the cause.

24Real-World Examples

Different teams use the Descheduler for different reasons.

Online shops with sales peaks

After a traffic peak, nodes added by the autoscaler stay under-used while older nodes stay busy. A balancing policy spreads the remaining load, and a compaction policy later lets surplus nodes be removed.

Teams using cheap spot machines

When spot machines disappear and come back, Pods bunch up on the survivors. The Descheduler spreads them again once the new machines are ready.

Platform teams that change node rules

When a platform team adds a taint to reserve nodes for a special workload, the taint and affinity plugins clear out Pods that no longer belong, without a manual drain.

High availability across zones

After a zone outage, Pods may be concentrated in the zones that stayed up. The topology spread plugin restores an even spread once the missing zone returns.

Cost-focused environments

Development and test clusters often run with many half-empty nodes. Compaction followed by node removal can lower the bill without touching production.

Clusters with long-lived Pods

Some teams use the lifetime plugin with a generous limit so that applications are refreshed regularly, picking up the newest images and settings without a manual rollout.

25Frequently Asked Questions

Short answers to questions that beginners often ask.

Q1Does the Descheduler schedule Pods?

No. It only evicts Pods. The scheduler is still the one that picks the new node for the replacement.

Q2Does the Descheduler run all the time?

It can, as a Deployment with an interval, or it can run on a schedule as a CronJob, or once as a Job. The best choice depends on how quickly you need drift to be corrected.

Q3Will it delete my important system Pods?

By default it does not evict system-critical Pods, DaemonSet Pods, static Pods, Pods with local storage, or Pods without an owner. You can adjust these rules, so be careful when you do.

Q4Does it respect PodDisruptionBudgets?

Yes, because it uses the Eviction API. If an eviction would break a budget, the API server refuses it, and the Pod stays where it is.

Q5Why did my Pod come back on the same node?

The scheduler decided that the same node was still the best choice. Add spread or affinity rules to the Pod, or make sure the scheduler’s preferences match the Descheduler’s goal.

Q6Can I try a policy without any risk?

Yes. Dry-run mode makes the Descheduler write what it would do to its logs without evicting anything.

Q7Is it based on real CPU use or on requests?

By default the utilization plugins use the resource requests of Pods. Newer versions can use live metrics, but this must be configured on purpose.

Q8How is it different from draining a node?

A drain empties one chosen node, usually for maintenance. The Descheduler looks across the whole cluster and evicts only Pods that break a policy.

Q9Do I need it if I use an autoscaler?

Not always. The autoscaler adds and removes nodes but does not move running Pods. The Descheduler helps when you need Pods rebalanced or packed so that nodes can actually be removed.

Q10Can I protect one particular Pod?

Yes. You can use priority levels, labels used by the evictor, or an annotation on the Pod to tell the Descheduler to leave it alone.

26Summary and Key Takeaways

Kubernetes places each Pod once and then trusts that placement for the Pod’s whole life. As nodes join and leave, rules change and usage shifts, those old decisions become less ideal. The Descheduler closes that gap. It reads a policy, inspects the cluster, nominates Pods that no longer belong where they are, filters out the ones that are unsafe to move, and asks the API server to evict the rest, one careful step at a time. The scheduler then chooses new homes for the replacements. Used with care, small limits and well-built applications, it keeps a cluster balanced, tidy and cost-aware without anyone doing it by hand.

Key Takeaways

  • It only evicts — the Descheduler never places Pods. The scheduler picks the new node, so the two must agree on the goal.
  • Drift is the problem — new nodes, changed labels and taints, and shifting usage make old placements stale, and core Kubernetes never revisits them.
  • Policy drives everything — profiles turn on plugins of the Deschedule and Balance types, and a filter decides who may be evicted.
  • Eviction is safe by design — it goes through the Eviction API, respects disruption budgets, and skips critical, DaemonSet, local-storage and ownerless Pods by default.
  • Choose spread or pack — LowNodeUtilization spreads load, HighNodeUtilization packs it for cost saving, and they should not be mixed on the same nodes.
  • Start small — begin with dry run, one plugin, low limits and a limited set of nodes, and widen only after watching the results.
  • Build robust applications — replicas, disruption budgets, readiness checks, graceful shutdown and spread rules make evictions harmless.