kubectl has a first-class plugin system (anything named kubectl-foo in your PATH becomes kubectl foo). Over the past year I have accumulated a handful of tiny scripts that solve small but recurring annoyances. Here are the four I use daily.

1. kubectl-events

kubectl get events sorted by lastTimestamp is a pain to remember. My plugin is one line:

#!/bin/sh
# kubectl-events
exec kubectl get events --sort-by=.lastTimestamp "$@"

Saved me many times when triaging. I often run:

kubectl events -A | tail -30

and get a time-ordered tail of the cluster’s recent events. This is the kind of thing you wish were default.

2. kubectl-why

When a pod is in Pending, I want to know why without digging. My kubectl-why pulls the most useful fields:

#!/bin/bash
# kubectl-why
ns=${KUBECTL_PLUGINS_CURRENT_NAMESPACE:-default}
pod="$1"
if [ -z "$pod" ]; then
  echo "usage: kubectl why <pod>"
  exit 1
fi

phase=$(kubectl -n "$ns" get pod "$pod" -o jsonpath='{.status.phase}')
echo "Phase: $phase"
echo
kubectl -n "$ns" get pod "$pod" -o jsonpath='{range .status.conditions[*]}{.type}: {.status} {.reason} {.message}{"\n"}{end}'
echo
echo "Events:"
kubectl -n "$ns" get events --field-selector involvedObject.name="$pod" --sort-by=.lastTimestamp | tail -10
echo
echo "Container statuses:"
kubectl -n "$ns" get pod "$pod" -o jsonpath='{range .status.containerStatuses[*]}{.name}: {.state} last: {.lastState}{"\n"}{end}'

Usage:

kubectl why my-pod-xyz
# Phase: Pending
# PodScheduled: False Unschedulable 0/5 nodes are available: 5 Insufficient memory.
# ...
# Events:
# 2m  Warning  FailedScheduling  default-scheduler  0/5 nodes are available

Probably 80% of “why is this pod not happy” questions get answered by this plugin’s output.

3. kubectl-nsh

kubectl exec -it POD -n NS -- bash is tedious when I want to drop into the first container in a pod quickly. Plus picking the right pod is annoying when there are multiple replicas.

#!/bin/bash
# kubectl-nsh
ns="${KUBECTL_PLUGINS_CURRENT_NAMESPACE:-default}"
selector=""
while [ $# -gt 0 ]; do
  case "$1" in
    -n|--namespace) ns="$2"; shift 2;;
    -l|--selector)  selector="-l $2"; shift 2;;
    *) pod="$1"; shift;;
  esac
done

if [ -n "$selector" ]; then
  pod=$(kubectl -n "$ns" get pod $selector -o jsonpath='{.items[0].metadata.name}')
fi

exec kubectl -n "$ns" exec -it "$pod" -- sh -c 'bash 2>/dev/null || sh'

Usage:

kubectl nsh -n app -l app=api

Picks the first pod matching the selector, shells into it, tries bash, falls back to sh.

4. kubectl-ages

Sometimes I want a quick column of “when were these pods created” sorted freshest first. I realize kubectl get pods shows age, but it does not sort by age, and a long-running environment is noisy. My plugin prints a clean sorted table:

#!/bin/bash
# kubectl-ages
kubectl get pods "$@" --sort-by=.metadata.creationTimestamp \
    -o custom-columns=NAME:.metadata.name,NS:.metadata.namespace,AGE:.metadata.creationTimestamp,PHASE:.status.phase \
    --no-headers | awk '{ cmd="date -d "$3" +%s"; cmd | getline t; close(cmd); now=systime(); age=now-t; printf "%-50s %-20s %8ds  %s\n", $1, $2, age, $4 }'

Output:

kubectl ages -A | head -10
# coredns-abc       kube-system    27400s  Running
# cert-manager-def  cert-manager   14400s  Running
# ...

The --sort-by plus custom-columns is close to what you want, but the conversion to a clean integer age field makes it easier to pipe into awk.

Installation

Drop all four into ~/.local/bin and make them executable. kubectl plugin system picks them up automatically. Verify with:

kubectl plugin list
# /home/merce/.local/bin/kubectl-ages
# /home/merce/.local/bin/kubectl-events
# /home/merce/.local/bin/kubectl-nsh
# /home/merce/.local/bin/kubectl-why

Some of these overlap with krew plugins that are more polished. kubectl-neat, kubectl-tree, kubectl-stern are the three I use from krew. The four above are narrow enough that a 20-line shell script is the right size.

What I did not write

  • A plugin for diffing live manifest vs last-applied. kubectl diff already exists, and server-side apply means I rarely need it.
  • A cordon/drain helper. kubectl drain is already the right verb.
  • A log follower across pods. stern does this so well that duplicating would be silly.

The test for “should this be a plugin” is “do I type the same 30+ character command more than once a day?”. If yes, make it a three-line script.

Reflection

These four plugins save me maybe 5 minutes a day individually, but more importantly they reduce the friction of doing the right lookup rather than an approximation. kubectl events sorted is the tool I want when something is going wrong; before I wrote the plugin, I would sometimes settle for unsorted events because the flag was annoying to remember. Small friction makes a difference.

If you have similar friction in your kubectl workflow, write the plugin. Related: see my post on a CRD design mistakes I made early on for more “I got sloppy with my k8s tooling” content.