Kubernetes Observability skill for Codex

Cluster monitoring that scrapes, alerts and scales. Codex installs or tunes kube-prometheus-stack from pinned, committed Helm values, makes ServiceMonitor, PodMonitor and PrometheusRule selection work across namespaces, and sizes retention and storage from measured series. It ships the core PromQL for CPU throttling, memory against limits, OOM kills, restarts and pending pods, and tunes the default kubernetes-mixin alerts by routing and scope instead of muting them.

  • Observability & SRE
  • $kubernetes-observability
  • 180 lines
  • 2 files
  • ~1,900 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- kubernetes-observability

Installs to ~/.agents/skills/kubernetes-observability/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $kubernetes-observability, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Sets up and tunes Kubernetes cluster monitoring with kube-prometheus-stack - Helm values for Prometheus Operator, Alertmanager, Grafana, node-exporter and kube-state-metrics, ServiceMonitor, PodMonitor and PrometheusRule resources that actually get selected, retention and storage sizing, default alert triage, and the key PromQL for CPU throttling, memory versus limits, OOM kills, restarts and pending pods. Use when installing cluster monitoring, when a ServiceMonitor is not scraped, when default Kubernetes alerts are noisy, or when building capacity views for nodes and workloads.

Use it when

  • A cluster needs Prometheus, Alertmanager and Grafana monitoring
  • ServiceMonitors, PodMonitors or PrometheusRules are not picked up
  • Default Kubernetes alerts are noisy or irrelevant on a managed cluster
  • You need queries for throttling, OOM kills, restarts and capacity

Use something else when

How to check the result

All expected targets are up, a new namespace's ServiceMonitor is scraped within minutes, Watchdog reaches the dead man's switch, and Prometheus memory and disk stay within limits after a day.

The mistake it prevents

Leaving the NilUsesHelmValues selectors on, so ServiceMonitors without the release label are silently ignored.

~/.agents/skills/kubernetes-observability/SKILL.md

--- name: kubernetes-observability description: Sets up and tunes Kubernetes cluster monitoring with kube-prometheus-stack - Helm values for Prometheus Operator, Alertmanager, Grafana,… license: MIT ---

Done well means: the cluster, nodes and every workload are scraped through declarative monitors, the default alerts are tuned to the cluster instead of muted, storage and retention are sized from measured series, and the core workload signals (throttling, memory against limits, OOM kills, restarts, scheduling) are one query away.

Inspect first

  1. Read AGENTS.md; find how the cluster is managed (Argo CD, Flux, Helmfile, Terraform helm_release) and whether kube-prometheus-stack or another stack (Grafana Alloy, Datadog, a managed Prometheus) is already installed.
  2. Read-only checks (need cluster access from the user’s kubeconfig):
kubectl config current-context
helm list -A | rg -i "prometheus|grafana|alloy|loki"
kubectl get prometheus,alertmanager,servicemonitor,podmonitor,prometheusrule -A
kubectl get crd | rg monitoring.coreos.com
  1. Confirm the context points at the intended cluster before any install or upgrade; installing into the wrong cluster is a classic incident. Helm installs and upgrades change the cluster and require the user’s explicit go-ahead.

Install or upgrade

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm upgrade --install kps prometheus-community/kube-prometheus-stack \
  -n monitoring --create-namespace -f values.yaml --version <pinned-version>

Pin the chart version and read its upgrade notes; major versions often require applying updated CRDs first (Helm does not upgrade CRDs in the crds/ folder). In GitOps setups, commit the values and let Argo CD or Flux apply them instead of running Helm by hand.

Core values:

prometheus:
  prometheusSpec:
    retention: 15d
    retentionSize: 45GB                      # below the volume size, leaves headroom
    replicas: 2
    resources:
      requests: { cpu: "1", memory: 8Gi }
      limits:   { memory: 12Gi }
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: gp3
          resources: { requests: { storage: 60Gi } }
    # select monitors from all namespaces, not only those labelled with the release name
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    ruleSelectorNilUsesHelmValues: false
    externalLabels: { cluster: prod-eu-1 }
alertmanager:
  alertmanagerSpec:
    replicas: 2
grafana:
  admin:
    existingSecret: grafana-admin           # never commit the admin password
  sidecar:
    dashboards: { enabled: true, label: grafana_dashboard, searchNamespace: ALL }
kube-state-metrics:
  metricLabelsAllowlist:
    - pods=[app.kubernetes.io/name,app.kubernetes.io/component]

Sizing: plan memory from active series (roughly a few KB per head series plus query headroom) and disk from samples per second x retention x ~1-2 bytes per sample after compression. Measure prometheus_tsdb_head_series after a day and adjust. For long retention or multiple clusters, remote-write to Thanos, Mimir or a managed backend rather than growing local disks.

Getting workloads scraped

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: checkout
  namespace: checkout
spec:
  selector:
    matchLabels: { app.kubernetes.io/name: checkout }
  endpoints:
    - port: metrics          # the Service port NAME, not the number
      path: /metrics
      interval: 30s

When a target does not appear, check in this order:

  1. The Prometheus resource’s serviceMonitorSelector and serviceMonitorNamespaceSelector match the monitor (kubectl get prometheus -n monitoring -o yaml). With the NilUsesHelmValues defaults left on, monitors need the label release: <helm release>.
  2. The ServiceMonitor selector matches the Service labels, and the Service has a named port matching endpoints.port.
  3. The Service has endpoints (kubectl get endpointslices -n checkout).
  4. Prometheus has RBAC to that namespace (the chart grants cluster-wide by default).
  5. Status in Prometheus UI under Status > Targets (or kubectl port-forward svc/kps-kube-prometheus-stack-prometheus 9090 -n monitoring); look for scrape errors.

Use PodMonitor for pods without a Service (jobs, DaemonSets with host ports). PrometheusRule resources need labels matching ruleSelector the same way.

Key queries

# CPU throttling ratio per container (above ~25% sustained hurts latency)
sum by (namespace, pod, container) (rate(container_cpu_cfs_throttled_periods_total{container!=""}[5m]))
/ sum by (namespace, pod, container) (rate(container_cpu_cfs_periods_total{container!=""}[5m]))

# memory working set vs limit (OOM kill happens at the limit)
max by (namespace, pod, container) (container_memory_working_set_bytes{container!=""})
/ max by (namespace, pod, container) (kube_pod_container_resource_limits{resource="memory"})

# containers whose last termination was an OOM kill
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1

# restarts in the last hour
increase(kube_pod_container_status_restarts_total[1h]) > 0

# pods stuck pending
sum by (namespace) (kube_pod_status_phase{phase="Pending"}) > 0

# requests vs allocatable: how much of the cluster is reserved
sum(kube_pod_container_resource_requests{resource="cpu"}) / sum(kube_node_status_allocatable{resource="cpu"})

container_memory_working_set_bytes is what the kubelet uses for eviction; usage includes reclaimable cache and overstates pressure.

Default alerts: tune, do not mute

The chart ships the kubernetes-mixin rules. Common adjustments:

AlertUsual issueTune
KubeCPUOvercommit, KubeMemoryOvercommitFires on autoscaled clustersDisable when the cluster autoscaler handles capacity
CPUThrottlingHighNoisy for batch and sidecarsRaise threshold or scope by namespace; route as ticket
KubePodCrashLooping, KubePodNotReadyPages for dev namespacesRoute by namespace label to team receivers
WatchdogMeant to always fireRoute to a dead man’s switch service, never silence
etcd*, KubeScheduler*, KubeControllerManager* on managed clustersControl plane not scrapeableDisable those components in values (kubeEtcd.enabled: false etc.)

Change via defaultRules.disabled or defaultRules.rules.<group>: false in values plus your own PrometheusRule replacements; avoid editing generated rules in-cluster. Route by namespace or a team label in Alertmanager; see $alerting-rules.

Verification

  • Status > Targets shows every expected job up; up == 0 returns nothing unexpected.
  • A test ServiceMonitor in a new namespace is picked up within a couple of minutes.
  • The Watchdog alert reaches the dead man’s switch; a test PrometheusRule fires and routes.
  • After 24 h, Prometheus memory is under 70% of its limit and disk usage projects within retentionSize with headroom.

Output

The values file diff, monitors and rules added, alert routing changes, sizing numbers (series, samples per second, disk and memory), and the targets/alerts verification.

Gotchas

  • Two Prometheus instances (an old one and the chart’s) scraping the same targets double load and confuse dashboards.
  • Monitoring namespace in the same cluster dies with the cluster; send the Watchdog to an external dead man’s switch and consider remote-write.
  • kubectl top uses metrics-server, not Prometheus; numbers differ in timing and window.
  • High-cardinality kube-state-metrics labels (all pod labels and annotations) are a top cause of series explosions; allowlist only what you query ($metrics-cardinality).

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Kubernetes Observability"
  short_description: "Cluster monitoring that scrapes, alerts and scales"
  default_prompt: "$kubernetes-observability Install kube-prometheus-stack on our EKS cluster with sensible retention and storage"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem