Intro
Kubernetes node pressure eviction is a critical mechanism that protects cluster stability when nodes run low on resources like memory, disk, or inodes. When thresholds are crossed, the kubelet proactively evicts pods to reclaim resources and prevent node failure. For platform teams, reacting manually to evictions is slow and error-prone. Automating the detection, response, and recovery workflows with CI/CD pipelines turns a chaotic incident into a controlled, repeatable process.
This guide is for developers, DevOps engineers, and technical startup teams who operate Kubernetes clusters and want to integrate eviction handling into their automation. You will learn how to identify eviction risks, configure safe thresholds, build pipelines that detect and respond to pressure, and verify that your automation works without causing more harm. Every section includes concrete commands, expected outputs, and failure signals so you can apply the patterns immediately.
The goal is operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify the result, and document recovery steps before an incident forces a decision.
Version and Environment Inventory
Before automating eviction workflows, you must know your cluster's version, node operating system, kubelet configuration, and the CI/CD tools available. Node pressure eviction behavior changed across Kubernetes versions, especially with the introduction of soft eviction thresholds and the evictionHard and evictionSoft settings.
Prerequisites:
- Kubernetes cluster version 1.20 or later (check with
kubectl version) - Access to node metrics (metrics-server or Prometheus)
- A CI/CD system (GitHub Actions, GitLab CI, Jenkins, etc.)
- Permissions to view nodes and pods (
kubectl get nodes,kubectl get pods) - Read access to kubelet configuration on nodes (typically
/var/lib/kubelet/config.yaml)
Start with a read-only inventory of your cluster state. Run these commands and capture the output:
# Check Kubernetes version
kubectl version --short
# List nodes and their conditions
kubectl get nodes -o wide
# Show node conditions including MemoryPressure and DiskPressure
kubectl describe node <node-name> | grep -A 5 Conditions
Expected output example:
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
MemoryPressure False Thu, 12 Oct 2023 10:15:32 +0000 Thu, 12 Oct 2023 09:45:11 +0000 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Thu, 12 Oct 2023 10:15:32 +0000 Thu, 12 Oct 2023 09:45:11 +0000 KubeletHasNoDiskPressure kubelet has sufficient disk available
PIDPressure False Thu, 12 Oct 2023 10:15:32 +0000 Thu, 12 Oct 2023 09:45:11 +0000 KubeletHasSufficientPID kubelet has sufficient PID available
If any condition is True, note the reason and message. For example, MemoryPressure may show KubeletHasInsufficientMemory with a message about available memory falling below the threshold.
Next, inspect the kubelet configuration on a node to see current eviction thresholds. SSH into a node (or use a privileged pod) and run:
cat /var/lib/kubelet/config.yaml | grep -A 10 eviction
Example output:
evictionHard:
memory.available: "200Mi"
nodefs.available: "10%"
nodefs.inodesFree: "5%"
imagefs.available: "15%"
evictionSoft:
memory.available: "500Mi"
nodefs.available: "15%"
evictionSoftGracePeriod:
memory.available: "1m30s"
nodefs.available: "2m"
Record these values. They define when eviction starts and how aggressive it is. Changes to these settings should be version-controlled and deployed through your CI/CD pipeline, not by hand-editing nodes.
Finally, check if you have monitoring in place. Query Prometheus for node memory and disk usage:
# Memory usage percentage per node
100 * (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes))
# Disk usage percentage for /var/lib/kubelet
100 * (1 - (node_filesystem_avail_bytes{mountpoint="/var/lib/kubelet"} / node_filesystem_size_bytes{mountpoint="/var/lib/kubelet"}))
Without metrics, you cannot detect pressure early enough to respond proactively.
Safe Configuration Path
Automating eviction response starts with safe configuration management. Never edit kubelet flags directly on a node; instead, use a configuration management tool (Ansible, Puppet, or a managed node pool) or a DaemonSet that updates the kubelet config file and restarts the kubelet in a controlled manner.
Principles for safe configuration:
- Separate observation from intervention: first collect metrics and logs, then decide whether to change thresholds.
- Limit blast radius: apply changes to one node or a small node pool first, verify, then roll out.
- Use placeholders for secrets: if your automation needs credentials (e.g., to drain a node), store them in a secret manager and inject them as environment variables, never in plain text.
- Verify the result: after a change, check node conditions and pod status to confirm the desired effect.
A concrete example: suppose you want to increase the hard memory eviction threshold from 200Mi to 500Mi to reduce evictions during transient spikes. The change should be made in a Git repository that holds your kubelet configuration templates. A pipeline could apply the change to a test node and check the impact.
Step-by-step safe configuration workflow:
- Create a configuration change in Git. For example, update a YAML file used by your config management:
# kubelet-config.yaml
evictionHard:
memory.available: "500Mi"
nodefs.available: "10%"
nodefs.inodesFree: "5%"
imagefs.available: "15%"
- Open a pull request. The CI pipeline runs a lint check and a dry-run against a test cluster. For example, using
kubectldry-run on a ConfigMap that mimics kubelet config:
kubectl create configmap kubelet-config-test --from-file=kubelet-config.yaml --dry-run=client -o yaml
- Merge the PR and deploy to a canary node. Use a job that copies the file and restarts kubelet on that node only:
# Example using Ansible
ansible-playbook -i inventory.ini update-kubelet.yml --limit node-canary-1
- Verify the node after restart. Check that the kubelet came back and the new threshold is in effect:
kubectl get node node-canary-1
kubectl describe node node-canary-1 | grep -A 5 Conditions
Also, inspect kubelet logs:
journalctl -u kubelet -n 50 --no-pager
Look for errors like failed to parse eviction thresholds or invalid eviction threshold.
- If verification passes, roll out to remaining nodes in batches, not all at once. Monitor cluster stability after each batch.
Owner and review frequency: Assign an owner for kubelet configuration changes, such as the platform engineering lead. Review thresholds quarterly or after any incident to ensure they align with workload changes.
Verification and Diagnostics
After configuring or responding to eviction pressure, you must verify that your automation works and that the cluster is healthy. This section gives you concrete commands and expected results for each step.
1. Check node pressure conditions
Run:
kubectl get nodes -o custom-columns=NAME:.metadata.name,MemoryPressure:.status.conditions[?(@.type=="MemoryPressure")].status,DiskPressure:.status.conditions[?(@.type=="DiskPressure")].status,PIDPressure:.status.conditions[?(@.type=="PIDPressure")].status
Expected output shows all False for healthy nodes. If any show True, investigate immediately.
2. Check evicted pods
List pods that have been evicted:
kubectl get pods --all-namespaces --field-selector=status.phase=Failed | grep Evicted
Or with a JSON query:
kubectl get pods --all-namespaces -o json | jq '.items[] | select(.status.reason=="Evicted") | .metadata.name + " in " + .metadata.namespace'
Example output:
my-app-7d5f8b9c4-abcde in default
cache-worker-6f4b7d8c9-fghij in staging
3. Check kubelet logs for eviction events
On a node, run:
journalctl -u kubelet | grep -i evict
Sample log line:
I1012 10:20:15.123456 1234 eviction_manager.go:340] "Evicting pod" pod="default/my-app-7d5f8b9c4-abcde" reason="memory.available" message="Pod uses more memory than its request and node is under memory pressure"
Note the reason and message. This tells you which resource triggered eviction and whether the pod exceeded its request.
4. Verify pod resource requests and limits
Often eviction happens because pods lack proper resource requests. Check a pod's requests:
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].resources}'
If requests are missing or too low, the kubelet cannot make informed eviction decisions.
5. Simulate pressure (dry-run) safely
You can simulate memory pressure on a test node by deploying a pod that consumes memory, but always do this in a non-production environment and with a clear rollback plan. Example:
apiVersion: v1
kind: Pod
metadata:
name: memory-eater
spec:
containers:
- name: stress
image: polinux/stress
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "2G", "--vm-hang", "1"]
Apply it, watch node conditions, and see if eviction occurs. This helps validate your thresholds and alerting.
6. Test your alerting pipeline
If you have Prometheus alerts for node pressure, trigger a test alert and confirm that your notification channel (Slack, PagerDuty, etc.) receives it. For example, a Prometheus alert rule:
groups:
- name: node-pressure
rules:
- alert: NodeMemoryPressure
expr: kube_node_status_condition{condition="MemoryPressure",status="true"} == 1
for: 5m
labels:
severity: warning
annotations:
summary: "Node {{ $labels.node }} is under memory pressure"
Verify that the alert fires and reaches the intended recipients.
Failure Modes and Recovery
Even with automation, things can go wrong. Here are common failure modes and how to recover from each.
Failure mode 1: Eviction loop (pod keeps getting evicted)
Why it happens: A pod has a resource request that is too high for available node capacity, or the node is persistently overcommitted. The scheduler places the pod, kubelet evicts it, and the pod gets rescheduled, repeating the cycle.
How to detect: Frequent evictions of the same pod in kubectl get pods output, and node pressure conditions flapping.
Recovery:
- Reduce the pod's resource requests to fit within node capacity.
- Add more nodes or increase node capacity.
- Use pod anti-affinity to spread pods across nodes.
- If the pod is not critical, lower its priority class.
Failure mode 2: Aggressive eviction causes service disruption
Why it happens: Hard eviction thresholds set too high (or low, depending on resource) cause the kubelet to evict pods too early, even when the node could have survived with soft eviction.
How to detect: Sudden drop in available pods for a service, increased 5xx errors, or user complaints.
Recovery:
- Adjust eviction thresholds to be less aggressive (e.g., increase the memory available threshold).
- Implement soft eviction with grace periods to allow pods to terminate gracefully.
- Set PodDisruptionBudgets to ensure a minimum number of pods remain available.
Failure mode 3: Automation makes changes without verification
Why it happens: The CI/CD pipeline applies configuration changes but does not check node conditions or pod status afterward. A bad change can propagate to all nodes, causing widespread eviction.
How to detect: Cluster-wide eviction events or node failures shortly after a deployment.
Recovery:
- Roll back the configuration change using your pipeline's rollback mechanism (e.g., revert the Git commit and redeploy).
- Ensure your pipeline includes a verification step with explicit checks before proceeding to the next batch.
- Use canary deployments for configuration changes.
Failure mode 4: Metrics missing or inaccurate
Why it happens: Monitoring components are not installed or misconfigured, so you cannot see node pressure until it's too late.
How to detect: No data in dashboards or alerts not firing when pressure occurs.
Recovery:
- Install metrics-server if absent:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml - Verify metrics-server is running:
kubectl get deployment metrics-server -n kube-system - Check node metrics:
kubectl top nodes
Example output:
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
node-1 350m 8% 1400Mi 38%
node-2 200m 5% 900Mi 25%
Failure mode 5: Evicted pods not rescheduled
Why it happens: After eviction, the scheduler may not reschedule the pod if there is no suitable node, or if the pod has a node selector that no longer matches any node.
How to detect: Pods stuck in Pending state after eviction.
Recovery:
- Check pod events:
kubectl describe pod <pod-name> - Check node capacity and taints:
kubectl describe node <node-name> - If necessary, manually delete the pod to force rescheduling, or scale the deployment.
Operations Checklist
Use this checklist before and after implementing eviction automation. Each item should have a clear owner and be revisited periodically.
| # | Item | Owner | Frequency | Verification command | Expected result |
|---|---|---|---|---|---|
| 1 | Verify Kubernetes version and kubelet configuration | Platform engineer, e.g., Priya Shah | Onboarding and quarterly | kubectl version --short, cat /var/lib/kubelet/config.yaml | Version >= 1.20, thresholds defined |
| 2 | Ensure metrics-server or Prometheus is collecting node metrics | Monitoring lead, e.g., Alex Chen | Monthly | kubectl top nodes, Prometheus queries | Metrics available for all nodes |
| 3 | Check node pressure conditions | On-call engineer (rotation) | Daily (automated check) | kubectl get nodes -o custom-columns=... | All conditions False |
| 4 | Review eviction events in logs | Platform engineer | Weekly | journalctl -u kubelet | grep -i evict (or centralized logging query) | No unexpected evictions |
| 5 | Test alerting pipeline | SRE lead, e.g., Maria Gomez | Quarterly | Trigger test alert in Prometheus | Alert received in Slack/PagerDuty |
| 6 | Validate rollback procedure for kubelet config changes | Platform engineer | Before any config change | Run pipeline rollback job on test node | Config reverts, node healthy |
| 7 | Review resource requests and limits for critical workloads | Application owners | Monthly | kubectl get pods -o jsonpath='{.spec.containers[*].resources}' | Requests match actual usage |
| 8 | Update runbooks with eviction recovery steps | Technical writer or platform lead | Quarterly | Review runbook document | Runbook current and actionable |
Common Pitfalls and How to Avoid Them
Several mistakes undermine eviction automation. Here are the most common ones and how to prevent or recover from them.
Pitfall 1: Hard-coding thresholds without understanding workload patterns
Many teams copy eviction thresholds from blog posts without analyzing their own node sizes and workload memory/disk usage. This leads to either premature evictions or node crashes.
How to avoid: Baseline your nodes' resource usage over at least two weeks, including peak and off-peak. Use monitoring data to set thresholds that allow for normal fluctuations.
Pitfall 2: Not accounting for image filesystem (imagefs) separately
Kubernetes can have separate filesystems for container images and container writable layers. If you only configure nodefs thresholds but not imagefs, the kubelet may not evict pods when the image filesystem fills up, leading to image pull failures.
How to avoid: Check your container runtime storage driver and set both nodefs and imagefs thresholds appropriately. Use df -h on nodes to identify mount points.
Pitfall 3: Ignoring soft eviction grace periods
Soft eviction allows pods to terminate gracefully within a grace period. Skipping soft eviction means pods are killed immediately, potentially losing in-flight requests.
How to avoid: Define soft eviction thresholds slightly above hard thresholds, and set grace periods long enough for your applications to drain connections (typically 1-5 minutes).
Pitfall 4: Automating response without human approval for critical changes
Fully automated remediation can make a bad situation worse if the automation misinterprets metrics and evicts too many pods. For example, an auto-scaler that removes nodes during a memory spike could trigger cascading evictions.
How to avoid: Implement a human approval step for actions that drain nodes or change kubelet configuration in production. Use a chat-ops tool where an engineer types a command to approve.
Pitfall 5: Not testing your CI/CD pipeline itself
Pipelines that are never tested may fail silently or not run at all. A broken pipeline gives false confidence.
How to avoid: Schedule regular pipeline runs (e.g., nightly) that execute a subset of checks against a staging cluster. Monitor pipeline success rates and alert on failures.
Conclusion
Automating Kubernetes node pressure eviction workflows with CI/CD transforms a reactive, stressful process into a controlled, repeatable one. By starting with a thorough environment inventory, implementing safe configuration changes, verifying with concrete commands, and planning for failure, you can keep your clusters stable even under resource pressure.
The key is to observe before changing, limit the blast radius, and always verify results. Use the operations checklist to assign ownership and ensure ongoing review. Avoid common pitfalls like hard-coding thresholds without baselining, ignoring soft eviction, or automating without safeguards.
As a next step, choose one low-risk verification from this guide—for example, checking node pressure conditions with a custom column command—record the current state, run the check, and compare the result with the expected output. Then, gradually integrate more automation into your pipeline, always keeping recovery in mind.
A reliable technical workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision.