Skip to content
ohsudo, Home
Kubernetes

Kubernetes pods "Evicted": find the pressure on the node, and stop it from coming back

Fix Kubernetes pods with status Evicted: read why with describe, handle DiskPressure and MemoryPressure on the node, clean up, set requests and limits, delete old pods.

Intermediate4 min readApplies to Kubernetes 1.28+ · kubectl · EKS · GKE · AKS

by OhSudo ··

Not sure this is your cause?Find it step by step: Pod not running →Find it step by step: Disk full or out of memory →

Part of a learning path:Kubernetes: debugging pods

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-7d9f8b6c4-x2k9p 0/1 Evicted 0 2h
myapp-7d9f8b6c4-b7m4q 0/1 Evicted 0 2h
myapp-7d9f8b6c4-t9n2c 1/1 Running 0 3m

A node ran short of disk or memory, and the kubelet removed pods to protect the node. A Deployment simply starts replacements somewhere else, so your application may look fine, with a growing list of dead pods behind it.

Those pods aren’t the problem. The node is. Clearing them up without looking at why just means more of them later.

  1. Nodedisk or memory runs short(breaks here)
  2. kubeletevicts pods
  3. Evicted podsreplacements start elsewhere
The node ran short of disk or memory, and its kubelet removed pods to protect it. The Deployment starts new ones elsewhere: the node is the problem, not the pods.

TL;DR

Terminal window
kubectl describe pod myapp-7d9f8b6c4-x2k9p | grep -A3 -E '^Status|Reason|Message'
kubectl get nodes
kubectl describe node worker-2 | grep -A8 Conditions
kubectl delete pods --field-selector=status.phase=Failed # clean up afterwards

1. Read why

Terminal window
kubectl describe pod myapp-7d9f8b6c4-x2k9p
Status: Failed
Reason: Evicted
Message: The node was low on resource: ephemeral-storage. Threshold quantity: 5251858022, available: 4029804Ki. Container myapp was using 4Gi, request is 0, has larger consumption of ephemeral-storage.

The message says what ran short, and which container used a lot of it. The threshold is in bytes: here, the default 10% of the node’s disk. Across the namespace:

Terminal window
kubectl get events --field-selector reason=Evicted --sort-by=.lastTimestamp

Events only last about an hour by default, while the pod’s message stays for as long as the pod does. So don’t delete them before you’ve read one.

Message mentions Means Go to
ephemeral-storage, nodefs, imagefs Disk Section 2
memory Memory Section 3
pid Too many processes Section 4
No shortage named, The node had condition A taint or node condition Section 5

2. DiskPressure

Terminal window
kubectl describe node worker-2 | grep -E 'DiskPressure|ephemeral-storage'
DiskPressure True KubeletHasDiskPressure kubelet has disk pressure

The kubelet’s default hard limits are nodefs.available<10% and imagefs.available<15%, among others. See what’s using the disk, on the node itself, through a debug pod:

Terminal window
kubectl debug node/worker-2 -it --image=ubuntu
chroot /host
df -h
du -xh --max-depth=1 /var/lib | sort -h | tail
crictl images

Three usual causes:

Container logs. An application that writes a lot to stdout fills /var/log/pods. Limit them with containerLogMaxSize and containerLogMaxFiles in the kubelet config, and make the application less talkative.

Writable layers and emptyDir. An application writing into its own filesystem, such as temp files or caches, counts as ephemeral storage. Find the biggest:

Terminal window
kubectl get --raw "/api/v1/nodes/worker-2/proxy/stats/summary" | \
python3 -c 'import json,sys; [print(p["ephemeral-storage"]["usedBytes"]//1048576, "MiB", p["podRef"]["name"]) for p in json.load(sys.stdin)["pods"]]' | sort -n | tail

Unused images. The kubelet cleans those itself, and sudo crictl rmi --prune does it now. A CI node that builds images needs this most.

No space left on device has more on hunting down space on a Linux machine. Then either make the disk bigger, or set limits, as in section 6.

3. MemoryPressure

Terminal window
kubectl describe node worker-2 | grep MemoryPressure
kubectl top nodes
kubectl top pods -A --sort-by=memory | head -n 15

When memory’s short, the kubelet evicts in this order, roughly: pods using more than they requested go first, and among those, by priority and how far above the request they are. So a pod with no requests at all is an easy target.

Memory shortage can also bring in the kernel’s OOM killer, which ends one process rather than the pod. That shows up as OOMKilled, with exit code 137: see that article.

4. PIDPressure

Terminal window
kubectl describe node worker-2 | grep PIDPressure

Something’s creating processes without limit, often a process that doesn’t clean up its children. Find it on the node with ps -eLf | wc -l and ps aux --sort=-nlwp | head. An init process in the container, or podSpec.shareProcessNamespace, may help with zombies.

5. Other reasons pods get removed

Not everything that’s removed is evicted by pressure:

  • Preempted: a higher-priority pod needed the room. The message says so.
  • The API or kubectl drain: a planned action, and it respects PodDisruptionBudgets.
  • Node shutdown: spot or preemptible nodes being reclaimed.
  • Over its own limit: a pod exceeding its ephemeral-storage limit is evicted even though the node is fine, with a message such as Pod ephemeral local storage usage exceeds the total limit of containers.

6. Set requests and limits

This is the lasting fix. Requests make the scheduler place pods where they fit, and lower their position in the eviction queue:

resources:
requests:
cpu: 100m
memory: 256Mi
ephemeral-storage: 1Gi
limits:
memory: 512Mi
ephemeral-storage: 2Gi

Set them from real use, as seen in kubectl top, rather than guessing. The Kubernetes Manifest Generator includes CPU and memory requests by default. For temp files, limit that as well:

volumes:
- name: tmp
emptyDir:
sizeLimit: 1Gi

Important workloads can have a higher priority with a PriorityClass. And add monitoring for node disk and memory, as it’s easy not to notice pods being replaced.

7. Clean up the dead pods

Evicted pods stay around so that you can read why. They use no resources besides an API object. Remove them when you’re done:

Terminal window
kubectl delete pods --field-selector=status.phase=Failed
kubectl delete pods -A --field-selector=status.phase=Failed

The first is for one namespace, and the second for all. Note that this removes all failed pods, including those from other failures, which you may want to look at first. Kubernetes only cleans them up by itself after a high count, 12,500 by default.

Warning

Raising the eviction thresholds, or turning them off, doesn’t add any disk or memory. Then the node itself runs out: the kubelet or the container runtime can stop working, and the node goes NotReady, taking down every pod on it. Evictions are the mild version of that failure.

Shell, we have a problem!

Similar, but different: Pending with Insufficient cpu means nothing has room for a new pod. Exit code 137 and OOMKilled mean the container hit a memory limit. NodeNotReady means the node’s stopped reporting. ImageGCFailed in events means the kubelet couldn’t free enough image space, and it comes with DiskPressure.

$ ./practice.sh

Practice this in the terminal

Open the Terminal Playground with this article’s scenario loaded. It is safe and runs in your browser.

Open the terminal →

Found this useful? Share it.

Found a mistake? Let us know at contact@ohsudo.com.

Tools

Try it yourself

Articles

Related articles