Kubernetes SRE Triage
Investigate Kubernetes and container incidents with a repeatable workflow. Start with read-only evidence collection, classify the failure mode, then choose the narrowest safe fix. Prefer Git-backed remediation when the workload is GitOps-managed.
Use when
- Pods are
Pending, CrashLoopBackOff, Error, or repeatedly restarting
- A rollout is stuck or a workload is unhealthy after deploy
- Ingress, service, DNS, TLS, or endpoint routing is broken
- PVCs, volumes, or storage mounts are failing
- Nodes are unhealthy or workloads are not scheduling
- Argo CD or another GitOps controller is blocked by runtime issues in-cluster
Do not use when
- The problem is primarily a CI log or PR check failure before deployment. Use the installed GitHub
github:gh-fix-ci skill for GitHub Actions, or investigate the owning pipeline natively.
- The problem is primarily Prometheus, Alertmanager, scrape health, or Grafana alert logic. Use
prometheus-grafana-triage.
- The user wants cluster provisioning, architecture design, or migration planning rather than incident response.
- The blocker is Argo CD or Flux sync/convergence state itself: use
gitops-reconcile.
Workflow
1. Establish scope
Capture:
- current kube context
- affected cluster, namespace, workload, or URL
- user-visible impact
- whether the workload is GitOps-managed
If the incident came from an alert or dashboard, identify the owning cluster before gathering pod evidence. Do not assume the current kube context is the one that produced the alert.
If the report is vague, ask one short clarifying question, then start gathering evidence.
2. Gather baseline state
Start with:
kubectl config current-context
kubectl get nodes
kubectl get pods -A
kubectl get events -A --sort-by=.metadata.creationTimestamp
Then narrow to the affected namespace and owner resource:
kubectl get deploy,statefulset,daemonset,job,cronjob -n <ns>
kubectl describe pod <pod> -n <ns>
kubectl logs <pod> -n <ns> --all-containers
kubectl logs <pod> -n <ns> -c <container> when a container is named
kubectl logs <pod> -n <ns> --all-containers --previous when restarts are involved
When the user names a specific pod or namespace/pod:container, make logs part
of the first evidence batch. Fetch the named container explicitly when present,
extract recent warnings and errors with timestamps, and summarize the log signal
before pod-status-only conclusions. Check --previous only when restart count
or lastState says a previous container should exist; if it is unavailable,
record that as evidence instead of silently skipping it.
Bundled helpers:
scripts/k8s_snapshot.sh for a fast cluster-wide baseline snapshot
scripts/pod_triage.sh <namespace> <pod> for pod-specific evidence
3. Classify the failure
Put the problem into one primary bucket before changing anything:
- scheduling: unschedulable, taints, quotas, missing PVC, node pressure
- runtime: crash loop, bad config, secret/env mismatch, probe failure
- networking: service selector mismatch, endpoints missing, ingress/TLS/DNS error
- storage: PVC pending, mount failures, permissions, volume exhaustion
- GitOps: desired state invalid, sync blocked, CRD/operator dependency missing
- provider dependency: identity, load balancer, cloud API, stopped cluster
Use the reference file for the likely bucket instead of branching into every theory at once.
4. Prefer GitOps-safe fixes
If the resource is GitOps-managed:
- inspect the manifest or Helm values in Git before making changes
- use live patches only when necessary to restore service quickly
- if a live fix is made, record the drift and reconcile it back into Git
Before any destructive, live infra mutation, immediately confirm the target context and namespace, preserve pre-fix evidence before restarting, deleting, or replacing failing pods, and require explicit approval before kubectl delete, kubectl patch, kubectl scale, and kubectl rollout restart. Pause before final submission until consent is confirmed, then execute with a rollback plan and blast-radius notes.
Do not leave the system in an undocumented drift state.
5. Verify recovery
After any fix:
- confirm pods are healthy and ready
- confirm services/endpoints/ingress resolve correctly
- confirm rollout has converged
- confirm the original symptom is gone
- if monitoring exists, confirm alerts or scrape failures have cleared
6. Summarize clearly
Report:
- symptom
- root cause
- fix applied or recommended
- verification evidence
- residual risk or follow-up work
Gotchas
- Separate symptom, root cause, and remediation. Do not blur them.
- Prefer a single evidence-backed diagnosis over several weak guesses.
- If you cannot prove root cause yet, say what is still unknown and what evidence would resolve it.
- Do not assume provider-specific behavior; verify it.
- For pod-log requests, lead with the log finding and the time window checked,
then use pod status, events, and owner resources to explain the surrounding
runtime state.
- When checking previous logs for a restart, verify the pod namespace first. A correct command in the wrong namespace is still bad evidence.
- Treat Secret values, service-account tokens, private keys, kubeconfig contents, and any other credential material as redacted. Quote Secret names and keys as evidence, never decoded values. Do not paste token values or private key fragments into findings or handoffs.
Related specialist skills
- Use
prometheus-grafana-triage when the incident starts from an alert, scrape failure, dashboard mismatch, or Prometheus/Grafana signal.
- Write custom PromQL, ratios, and histogram queries directly; use
prometheus-cardinality-troubleshooter when a query is expensive because of label cardinality.
- Use
loki when the next step is LogQL, log parsers, Loki pipeline behavior, or log-derived metrics.
- Use
gitops-reconcile when the runtime issue is caused by Argo CD or Flux convergence rather than cluster mechanics.
- When the result is a monitoring design gap rather than a live runtime fix, design the telemetry change directly and record it as a durable decision.
- Use installed Codex Security plugin skills when the evidence suggests
compromise rather than a fault: unexpected workloads, privilege changes, or
credential misuse.
References
- Read
references/failure-modes.md when narrowing a Kubernetes runtime issue.
Scripts
scripts/k8s_snapshot.sh
Collects a deterministic snapshot of:
- current context
- nodes
- pods
- recent events
- services, ingress, PVCs, jobs, and cronjobs
Usage:
bash "${CODEX_HOME:-$HOME/.codex}/skills/k8s-sre-triage/scripts/k8s_snapshot.sh"
scripts/pod_triage.sh
Collects pod-specific evidence:
- owner references
describe
- current logs
- previous logs
Usage:
bash "${CODEX_HOME:-$HOME/.codex}/skills/k8s-sre-triage/scripts/pod_triage.sh" <namespace> <pod>
1---2name: k8s-sre-triage3description: Investigate Kubernetes and container runtime incidents with evidence-first triage. Use when pods are crashing or pending, rollouts are stuck, ingress or DNS routing is broken, storage or nodes are failing, or a GitOps-managed workload is unhealthy after deploy; prefer GitOps-safe remediation and verify recovery.4---56# Kubernetes SRE Triage78Investigate Kubernetes and container incidents with a repeatable workflow. Start with read-only evidence collection, classify the failure mode, then choose the narrowest safe fix. Prefer Git-backed remediation when the workload is GitOps-managed.910## Use when1112- Pods are `Pending`, `CrashLoopBackOff`, `Error`, or repeatedly restarting13- A rollout is stuck or a workload is unhealthy after deploy14- Ingress, service, DNS, TLS, or endpoint routing is broken15- PVCs, volumes, or storage mounts are failing16- Nodes are unhealthy or workloads are not scheduling17- Argo CD or another GitOps controller is blocked by runtime issues in-cluster1819## Do not use when2021- The problem is primarily a CI log or PR check failure before deployment. Use the installed GitHub `github:gh-fix-ci` skill for GitHub Actions, or investigate the owning pipeline natively.22- The problem is primarily Prometheus, Alertmanager, scrape health, or Grafana alert logic. Use `prometheus-grafana-triage`.23- The user wants cluster provisioning, architecture design, or migration planning rather than incident response.24- The blocker is Argo CD or Flux sync/convergence state itself: use `gitops-reconcile`.2526## Workflow2728### 1. Establish scope2930Capture:31- current kube context32- affected cluster, namespace, workload, or URL33- user-visible impact34- whether the workload is GitOps-managed3536If the incident came from an alert or dashboard, identify the owning cluster before gathering pod evidence. Do not assume the current kube context is the one that produced the alert.3738If the report is vague, ask one short clarifying question, then start gathering evidence.3940### 2. Gather baseline state4142Start with:43- `kubectl config current-context`44- `kubectl get nodes`45- `kubectl get pods -A`46- `kubectl get events -A --sort-by=.metadata.creationTimestamp`4748Then narrow to the affected namespace and owner resource:49- `kubectl get deploy,statefulset,daemonset,job,cronjob -n <ns>`50- `kubectl describe pod <pod> -n <ns>`51- `kubectl logs <pod> -n <ns> --all-containers`52- `kubectl logs <pod> -n <ns> -c <container>` when a container is named53- `kubectl logs <pod> -n <ns> --all-containers --previous` when restarts are involved5455When the user names a specific pod or `namespace/pod:container`, make logs part56of the first evidence batch. Fetch the named container explicitly when present,57extract recent warnings and errors with timestamps, and summarize the log signal58before pod-status-only conclusions. Check `--previous` only when restart count59or `lastState` says a previous container should exist; if it is unavailable,60record that as evidence instead of silently skipping it.6162Bundled helpers:63- `scripts/k8s_snapshot.sh` for a fast cluster-wide baseline snapshot64- `scripts/pod_triage.sh <namespace> <pod>` for pod-specific evidence6566### 3. Classify the failure6768Put the problem into one primary bucket before changing anything:69- scheduling: unschedulable, taints, quotas, missing PVC, node pressure70- runtime: crash loop, bad config, secret/env mismatch, probe failure71- networking: service selector mismatch, endpoints missing, ingress/TLS/DNS error72- storage: PVC pending, mount failures, permissions, volume exhaustion73- GitOps: desired state invalid, sync blocked, CRD/operator dependency missing74- provider dependency: identity, load balancer, cloud API, stopped cluster7576Use the reference file for the likely bucket instead of branching into every theory at once.7778### 4. Prefer GitOps-safe fixes7980If the resource is GitOps-managed:81- inspect the manifest or Helm values in Git before making changes82- use live patches only when necessary to restore service quickly83- if a live fix is made, record the drift and reconcile it back into Git8485Before any destructive, live infra mutation, immediately confirm the target context and namespace, preserve pre-fix evidence before restarting, deleting, or replacing failing pods, and require explicit approval before `kubectl delete`, `kubectl patch`, `kubectl scale`, and `kubectl rollout restart`. Pause before final submission until consent is confirmed, then execute with a rollback plan and blast-radius notes.8687Do not leave the system in an undocumented drift state.8889### 5. Verify recovery9091After any fix:92- confirm pods are healthy and ready93- confirm services/endpoints/ingress resolve correctly94- confirm rollout has converged95- confirm the original symptom is gone96- if monitoring exists, confirm alerts or scrape failures have cleared9798### 6. Summarize clearly99100Report:101- symptom102- root cause103- fix applied or recommended104- verification evidence105- residual risk or follow-up work106107## Gotchas108109- Separate symptom, root cause, and remediation. Do not blur them.110- Prefer a single evidence-backed diagnosis over several weak guesses.111- If you cannot prove root cause yet, say what is still unknown and what evidence would resolve it.112- Do not assume provider-specific behavior; verify it.113- For pod-log requests, lead with the log finding and the time window checked,114 then use pod status, events, and owner resources to explain the surrounding115 runtime state.116- When checking previous logs for a restart, verify the pod namespace first. A correct command in the wrong namespace is still bad evidence.117- Treat Secret values, service-account tokens, private keys, kubeconfig contents, and any other credential material as redacted. Quote Secret names and keys as evidence, never decoded values. Do not paste token values or private key fragments into findings or handoffs.118119## Related specialist skills120121- Use `prometheus-grafana-triage` when the incident starts from an alert, scrape failure, dashboard mismatch, or Prometheus/Grafana signal.122- Write custom PromQL, ratios, and histogram queries directly; use `prometheus-cardinality-troubleshooter` when a query is expensive because of label cardinality.123- Use `loki` when the next step is LogQL, log parsers, Loki pipeline behavior, or log-derived metrics.124- Use `gitops-reconcile` when the runtime issue is caused by Argo CD or Flux convergence rather than cluster mechanics.125- When the result is a monitoring design gap rather than a live runtime fix, design the telemetry change directly and record it as a durable decision.126- Use installed Codex Security plugin skills when the evidence suggests127 compromise rather than a fault: unexpected workloads, privilege changes, or128 credential misuse.129130## References131132- Read `references/failure-modes.md` when narrowing a Kubernetes runtime issue.133134## Scripts135136### `scripts/k8s_snapshot.sh`137138Collects a deterministic snapshot of:139- current context140- nodes141- pods142- recent events143- services, ingress, PVCs, jobs, and cronjobs144145Usage:146```bash147bash "${CODEX_HOME:-$HOME/.codex}/skills/k8s-sre-triage/scripts/k8s_snapshot.sh"148```149150### `scripts/pod_triage.sh`151152Collects pod-specific evidence:153- owner references154- `describe`155- current logs156- previous logs157158Usage:159```bash160bash "${CODEX_HOME:-$HOME/.codex}/skills/k8s-sre-triage/scripts/pod_triage.sh" <namespace> <pod>161```