RAPID INCIDENT TRIAGE & ACTION PLAN•Performance
01. Error Signature
K8s DiskPressure Container Log Purger
02. Root Cause Vector

Worker nodes hit DiskPressure evicting pods when container logs consume 80%+ of node disk, triggering Kubernetes eviction loops that casc...

03. Rapid Prescription

Inspect container exit codes via kubectl describe, verify entrypoint and readiness probes.

Zero-Trust Client IDE/ Performance

K8s DiskPressure Container Log Purger

Worker nodes hit DiskPressure evicting pods when container logs consume 80%+ of node disk, triggering Kubernetes eviction loops that cascade into full cluster instability.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(96L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: v1
kind: ConfigMap
metadata:
  name: legacy-logging-policy
  namespace: pci-secure-zone
  annotations:
    owner: "team-legacy@my-registry.internal"
    # TODO: This was a quick fix for audit, need to refactor to use sidecars
    # OLD: retention: 7d
    # NEW: retention: dynamic based on disk usage (not implemented yet)
data:
  log_retention_days: "30"  # VULNERABILITY: Static retention with no cleanup mechanism on high disk usage
  # Below secrets are from the old vault migration, rotate after Q4
  aws_access_key_id: "AKIAIOSFODNN7EXAMPLE"
  aws_secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
  stripe_live_key: "sk_live_4eC39HqLyjWDarjtT1zdp7dc"
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: container-log-purger
  namespace: pci-secure-zone
  labels:
    app: log-cleanup
    # legacy-tag: do-not-remove (compliance requirement)
spec:
  selector:
    matchLabels:
      app: log-cleanup
  template:
    metadata:
      labels:
        app: log-cleanup
    spec:
      serviceAccountName: legacy-sa
      containers:
      - name: purger
        image: my-registry.internal/legacy-api:log-purge-v2.1
        # TODO: Image uses alpine:3.12 (EOL), upgrade after PCI audit
        env:
        - name: RETENTION_DAYS
          valueFrom:
            configMapKeyRef:
              name: legacy-logging-policy
              key: log_retention_days
        - name: DB_URI
          value: "postgresql://admin:SuperSecret123!@pci-db.internal:5432/audit_logs?sslmode=disable"
        - name: SSH_PRIVATE_KEY
          value: |
            -----BEGIN RSA PRIVATE KEY-----
            MIIEowIBAAKCAQEArX8M4gFc6H6gK7Pq1QzLmJkKjHqWzXpOqRstYwZabcdefghijklmn
            opqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnop
            qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqr
            stuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqrst
            uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqrstuv
            -----END RSA PRIVATE KEY-----
        command: ["/bin/sh"]
        args:
        - "-c"
        - |
          # This script is a mess but it works... mostly.
          # OLD LOGIC: find /var/log/containers -name "*.log" -mtime +$RETENTION_DAYS -delete
          # NEW LOGIC: Should trigger on disk usage >75%, but cron is broken
          while true; do
            USAGE=$(df /var/lib/docker | tail -1 | awk '{print $5}' | sed 's/%//')
            if [ "$USAGE" -gt 75 ]; then
              echo "Disk usage high: $USAGE%, purging logs..."
              # VULNERABILITY: Deletes logs older than 1 day ONLY, ignoring retention, causing rapid refill
              find /var/log/containers -name "*.log" -mtime +1 -delete
              sleep 300  # Wait 5 minutes, but logs may accumulate faster
            else
              sleep 60  # Check every minute, but high usage may persist
            fi
          done
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "128Mi"
            cpu: "100m"
        volumeMounts:
        - name: varlog
          mountPath: /var/log/containers
          readOnly: false
      volumes:
      - name: varlog
        hostPath:
          path: /var/log/containers
          type: DirectoryOrCreate
      # nodeSelector: {}  # Was set to {disk: ssd} but caused scheduling issues
      tolerations:
      - key: "node.kubernetes.io/disk-pressure"
        operator: "Exists"
        effect: "NoSchedule"  # This pod tolerates pressure, but doesn't resolve it effectively
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Fix Kubernetes DiskPressure Pod Evictions Caused by Container Log Accumulation

Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 15–30 mins

TL;DR

  • What broke: Kubernetes worker nodes crossed the imagefs.available or nodefs.available eviction threshold because container log files under /var/log/pods/ or /var/lib/docker/containers/ were never rotated, consuming 80%+ of node disk.
  • How to fix it: Enforce containerLogMaxSize and containerLogMaxFiles in the kubelet config, and immediately purge orphaned log files with find + truncate.
  • Fast path: Use our Client-Side Sandbox above to auto-refactor your kubelet ConfigMap or container spec — paste your config and get corrected YAML instantly.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What Does the Error Mean?)

Raw kubelet event from kubectl describe node <node-name>:

Conditions:
  Type              Status
  ----              ------
  DiskPressure      True

Events:
  Warning  Evicted   pod/api-server-7d9f4b8c6-xk2p9   
  The node had condition: DiskPressure

Raw eviction event from kubectl get events --field-selector reason=Evicted -A:

WARNING  Evicting pod api-server-7d9f4b8c6-xk2p9 
because it exceeded its local ephemeral storage limit 
or the node is under DiskPressure.

Immediate consequence: The kubelet's eviction manager fires. Pods are terminated without a graceful drain. Deployments restart pods onto other nodes — which are likely suffering the same log bloat — and the eviction cascades cluster-wide. Stateful workloads without PVCs lose ephemeral data permanently.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

This is a cascading resource exhaustion failure, not a single-node problem.

Why it escalates fast:

  1. No log rotation by default in many kubelet configs. If containerLogMaxSize is unset, a single verbose container (e.g., Istio sidecar, a Java app dumping stack traces) can write gigabytes in hours.
  2. /var/log/pods/ symlinks to /var/lib/docker/containers/ or containerd's content store. Deleting the pod does NOT immediately reclaim disk — the log file inode stays alive until the runtime GCs it.
  3. Evicted pods leave orphaned log directories. /var/log/pods/<namespace>_<pod>_<uid>/ persists after eviction. On a busy node cycling through evictions, these accumulate rapidly.
  4. Eviction threshold breach triggers a feedback loop: Eviction → new pod scheduled → new logs → disk fills faster → more evictions. Nodes can become NotReady within minutes.
  5. DaemonSets are not evicted by default, meaning your log shippers (Fluentd, Filebeat) stay running and continue writing their own buffers to the same full disk.

Blast radius: Full node NotReady, cluster autoscaler spinning up replacement nodes that inherit the same misconfiguration, SLA breach, potential data loss for non-PVC workloads.


How to Fix It

Step 0: Immediate Triage (Run This First)

# Identify top disk consumers on the affected node (run via node shell or SSH)
du -sh /var/log/pods/* | sort -rh | head -20
du -sh /var/lib/containerd/* | sort -rh | head -10

# Truncate (do NOT delete — deletion of open files won't free inodes) active log files over 500MB
find /var/log/pods/ -name '*.log' -size +500M -exec truncate -s 0 {} \;

# Force kubelet to GC dead containers
kubectl delete pod --field-selector=status.phase=Failed -A

Basic Fix: Kubelet Log Rotation Config

Edit the kubelet config on each affected node. Location is typically /etc/kubernetes/kubelet-config.yaml or managed via a ConfigMap in kubeadm clusters.

 apiVersion: kubelet.config.k8s.io/v1beta1
 kind: KubeletConfiguration
-# containerLogMaxSize and containerLogMaxFiles not set (defaults: 10Mi / 5)
+containerLogMaxSize: "50Mi"
+containerLogMaxFiles: 3
 evictionHard:
-  nodefs.available: "5%"
+  nodefs.available: "10%"
+  nodefs.inodesFree: "5%"
+imageGCHighThresholdPercent: 75
+imageGCLowThresholdPercent: 70

After editing, restart kubelet:

systemctl daemon-reload && systemctl restart kubelet

Enterprise Best Practice: Enforce via DaemonSet + Logrotate + Resource Quotas

1. Container-level log limits in the Pod spec (defense-in-depth):

 containers:
 - name: api-server
   image: myapp:v2.1.0
+  resources:
+    limits:
+      ephemeral-storage: "2Gi"
+    requests:
+      ephemeral-storage: "512Mi"

2. Cluster-level enforcement via LimitRange:

+apiVersion: v1
+kind: LimitRange
+metadata:
+  name: ephemeral-storage-limits
+  namespace: production
+spec:
+  limits:
+  - type: Container
+    max:
+      ephemeral-storage: "4Gi"
+    default:
+      ephemeral-storage: "1Gi"
+    defaultRequest:
+      ephemeral-storage: "256Mi"

3. Node-level logrotate DaemonSet for legacy containerd/docker log paths:

+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+  name: log-purger
+  namespace: kube-system
+spec:
+  selector:
+    matchLabels:
+      app: log-purger
+  template:
+    spec:
+      tolerations:
+      - operator: Exists  # Run even on tainted/pressured nodes
+      hostPID: true
+      containers:
+      - name: log-purger
+        image: alpine:3.19
+        command:
+        - /bin/sh
+        - -c
+        - |
+          while true; do
+            find /host/var/log/pods -name '*.log' -size +200M \
+              -exec truncate -s 0 {} \;
+            find /host/var/log/pods -maxdepth 3 -type d -empty -delete
+            sleep 300
+          done
+        volumeMounts:
+        - name: varlog
+          mountPath: /host/var/log
+      volumes:
+      - name: varlog
+        hostPath:
+          path: /var/log

💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

1. OPA/Gatekeeper policy — block pods with no ephemeral-storage limit:

package kubernetes.admission

deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not container.resources.limits["ephemeral-storage"]
  msg := sprintf("Container '%v' must define ephemeral-storage limit", [container.name])
}

2. Checkov scan in your pipeline:

# Fails pipeline if ephemeral-storage limits are missing
checkov -d ./k8s-manifests \
  --check CKV_K8S_20 \
  --check CKV_K8S_11

3. Prometheus alerting — fire BEFORE eviction threshold is hit:

- alert: NodeDiskPressureImminent
  expr: |
    (node_filesystem_avail_bytes{mountpoint="/"} /
     node_filesystem_size_bytes{mountpoint="/"}) < 0.15
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: "Node {{ $labels.instance }} disk below 15% — eviction imminent"

4. Kubelet config drift detection with kube-bench:

kube-bench node --check 4.2.10,4.2.11
# Validates containerLogMaxSize and containerLogMaxFiles are set

Set containerLogMaxSize: 50Mi and containerLogMaxFiles: 3 as your org-wide baseline. Enforce it via your node bootstrap scripts (Terraform user_data, Ansible, or EKS managed node group launch templates). Any node that drifts from this config should be flagged by your configuration management pipeline before it joins the cluster.

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Why does deleting an evicted pod not free up disk space immediately?
The root cause is a fundamental gap in container lifecycle management: when a pod is evicted or deleted, the container runtime (e.g., containerd) retains an open file descriptor to the active log file until garbage collection (GC) runs. This means the inode and disk blocks remain allocated, keeping your node in DiskPressure. While you can manually truncate the live file or force a GC cycle, these are reactive, high-touch fixes that fail to address the systemic logging flaw causing the bloat. This is a classic symptom of an environment lacking proactive, policy-driven log management at the container layer.
Q2.What is the default Kubernetes eviction threshold for disk and how do I check what mine is set to?
Defaults vary dangerously: often `nodefs.available: 5%` for hard eviction. Manually checking and adjusting these thresholds across a fleet is a high-risk, error-prone trap. A misconfigured value can either trigger premature, cascading pod evictions (causing application downtime) or delay evictions until a node is completely full, leading to kernel panics and a total node loss—a massive blast radius. Instead of playing Russian roulette with YAML, drop your broken node configuration or kubelet manifest into the StackEngine Local Sandbox above. It will automatically analyze, simulate, and compile a zero-trust, validated fix that enforces safe thresholds without compromising node stability.
Q3.How do I prevent log bloat from Istio sidecars specifically, since I cannot modify their container specs directly?
Mitigating log bloat from opaque components like Istio sidecars exposes a critical enterprise scaling problem. While you can tweak MeshConfigs or enforce kubelet log rotation, manually synchronizing these patches across hundreds of clusters, namespaces, and GitOps repositories is a compliance and audit nightmare. A single drift or misapplied label selector can leave entire environments vulnerable. For reliable, scalable control, upgrade to the StackEngine CI/CD Automator (Team Edition). It provides on-prem, automated pull request generation with Maker-Checker review, embedding security-as-code directly into your pipelines to enforce log policies uniformly across all workloads—including sidecars—ensuring continuous compliance without manual toil.