Kubernetes
PODs
Deletion
Age-based Management
Creation Time

Kubernetes How to delete PODs based on age/creation time

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Kubernetes does not have a built-in command to delete pods by age. To delete pods older than a certain threshold, you combine kubectl get pods with field selectors, JSONPath output, and shell date arithmetic to filter by creation timestamp. For automated cleanup, Kubernetes provides TTL controllers for finished Jobs and CronJobs for periodic cleanup scripts. This article covers manual deletion commands, automated approaches, and namespace-wide cleanup strategies.

Manual Deletion: Pods Older Than N Hours

bash
1# Delete pods older than 24 hours in the current namespace
2kubectl get pods -o json | \
3  jq -r '.items[] |
4    select(
5      (now - (.metadata.creationTimestamp | fromdateiso8601)) > 86400
6    ) | .metadata.name' | \
7  xargs kubectl delete pod

This uses jq to:

  1. Parse the creation timestamp of each pod
  2. Compare it to the current time
  3. Select pods where the age exceeds 86400 seconds (24 hours)
  4. Pipe the pod names to kubectl delete pod

Using kubectl with awk

Without jq, use kubectl output with awk and date:

bash
1# Delete pods older than 2 hours
2MAX_AGE=7200  # seconds
3
4kubectl get pods --no-headers -o custom-columns=\
5NAME:.metadata.name,CREATED:.metadata.creationTimestamp | \
6while read name created; do
7  created_epoch=$(date -d "$created" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s)
8  now_epoch=$(date +%s)
9  age=$((now_epoch - created_epoch))
10  if [ "$age" -gt "$MAX_AGE" ]; then
11    echo "Deleting $name (age: ${age}s)"
12    kubectl delete pod "$name"
13  fi
14done

Note: The date -d syntax works on Linux (GNU date). On macOS, use date -j -f or install gdate from coreutils.

Deleting by Pod Status and Age

bash
1# Delete Completed and Failed pods older than 1 hour
2kubectl get pods --field-selector=status.phase==Succeeded -o json | \
3  jq -r '.items[] |
4    select((now - (.metadata.creationTimestamp | fromdateiso8601)) > 3600) |
5    .metadata.name' | \
6  xargs -r kubectl delete pod
7
8kubectl get pods --field-selector=status.phase==Failed -o json | \
9  jq -r '.items[] |
10    select((now - (.metadata.creationTimestamp | fromdateiso8601)) > 3600) |
11    .metadata.name' | \
12  xargs -r kubectl delete pod

Deleting Evicted Pods

Evicted pods consume etcd storage without running. Clean them up:

bash
1# Delete all evicted pods (any age)
2kubectl get pods --all-namespaces --field-selector=status.phase==Failed -o json | \
3  jq -r '.items[] | select(.status.reason == "Evicted") |
4    "\(.metadata.namespace) \(.metadata.name)"' | \
5  while read ns name; do
6    kubectl delete pod -n "$ns" "$name"
7  done
8
9# One-liner for current namespace
10kubectl get pods | grep Evicted | awk '{print $1}' | xargs kubectl delete pod

TTL Controller for Finished Jobs

Kubernetes has a built-in TTL controller that automatically cleans up finished Jobs:

yaml
1apiVersion: batch/v1
2kind: Job
3metadata:
4  name: data-processor
5spec:
6  ttlSecondsAfterFinished: 3600  # Delete 1 hour after completion
7  template:
8    spec:
9      containers:
10        - name: processor
11          image: myapp:latest
12          command: ["python", "process.py"]
13      restartPolicy: Never

ttlSecondsAfterFinished automatically deletes the Job and its pods after the specified number of seconds. This is the cleanest solution for Job-based workloads.

CronJob for Automated Cleanup

Create a CronJob that periodically cleans up old pods:

yaml
1apiVersion: batch/v1
2kind: CronJob
3metadata:
4  name: pod-cleanup
5  namespace: default
6spec:
7  schedule: "0 */6 * * *"  # Every 6 hours
8  jobTemplate:
9    spec:
10      template:
11        spec:
12          serviceAccountName: pod-cleanup-sa
13          containers:
14            - name: cleanup
15              image: bitnami/kubectl:latest
16              command:
17                - /bin/sh
18                - -c
19                - |
20                  kubectl get pods --field-selector=status.phase==Succeeded \
21                    -o jsonpath='{.items[*].metadata.name}' | \
22                    tr ' ' '\n' | xargs -r kubectl delete pod
23                  kubectl get pods --field-selector=status.phase==Failed \
24                    -o jsonpath='{.items[*].metadata.name}' | \
25                    tr ' ' '\n' | xargs -r kubectl delete pod
26          restartPolicy: OnFailure

The CronJob needs a ServiceAccount with permission to list and delete pods:

yaml
1apiVersion: v1
2kind: ServiceAccount
3metadata:
4  name: pod-cleanup-sa
5---
6apiVersion: rbac.authorization.k8s.io/v1
7kind: Role
8metadata:
9  name: pod-cleanup-role
10rules:
11  - apiGroups: [""]
12    resources: ["pods"]
13    verbs: ["get", "list", "delete"]
14---
15apiVersion: rbac.authorization.k8s.io/v1
16kind: RoleBinding
17metadata:
18  name: pod-cleanup-binding
19subjects:
20  - kind: ServiceAccount
21    name: pod-cleanup-sa
22roleRef:
23  kind: Role
24  name: pod-cleanup-role
25  apiGroup: rbac.authorization.k8s.io

Using kubectl sort-by for Oldest Pods

bash
1# List pods sorted by creation time (oldest first)
2kubectl get pods --sort-by=.metadata.creationTimestamp
3
4# Show age column
5kubectl get pods -o custom-columns=\
6NAME:.metadata.name,\
7AGE:.metadata.creationTimestamp,\
8STATUS:.status.phase
9
10# Delete the 5 oldest pods
11kubectl get pods --sort-by=.metadata.creationTimestamp \
12  --no-headers -o custom-columns=NAME:.metadata.name | \
13  head -5 | xargs kubectl delete pod

Common Pitfalls

  • Deleting pods managed by a Deployment or ReplicaSet: Deleting a pod that belongs to a Deployment causes the controller to immediately create a replacement. To stop pods permanently, scale the Deployment to zero (kubectl scale deployment myapp --replicas=0) or delete the Deployment itself.
  • Using xargs without -r on empty input: If the jq filter matches no pods, xargs kubectl delete pod runs with no arguments and may produce an error. Use xargs -r (GNU) to skip execution when input is empty.
  • Date parsing differences between Linux and macOS: GNU date -d parses ISO timestamps directly, but macOS date requires -j -f with a format string. Scripts that work on Linux CI may fail on developer macOS machines.
  • Not scoping cleanup to a namespace: Running cleanup scripts without -n namespace operates on the current context's default namespace. Use --all-namespaces intentionally or always specify -n to avoid accidentally deleting pods in the wrong namespace.
  • Forgetting RBAC for CronJob-based cleanup: The cleanup CronJob's ServiceAccount needs explicit permissions to list and delete pods. Without a proper Role and RoleBinding, the cleanup pod fails with a 403 Forbidden error.

Summary

  • Kubernetes has no built-in "delete pods by age" command — use kubectl get pods -o json with jq or shell date arithmetic
  • Use ttlSecondsAfterFinished on Jobs for automatic cleanup of finished Job pods
  • Create a CronJob with kubectl for periodic automated cleanup of old pods
  • Delete evicted and completed pods separately using --field-selector=status.phase
  • Always consider whether pods are managed by a controller — deleting managed pods triggers immediate recreation
  • Use RBAC (ServiceAccount, Role, RoleBinding) for CronJob-based cleanup

Course illustration
Course illustration

All Rights Reserved.