A working, end-to-end demonstration of two open-source agentic AI tools for Kubernetes. The real k8sgpt and kubectl binaries run against a mock Kubernetes API server faithful enough that they can't tell the difference: simulated cluster, real binaries, real writes. An optional LLM backend writes the root-cause analysis, and every command on this page is one you can run.
Real kubectl remediation, end to end. k8sgpt reports 8 findings against the simulated payment-prod cluster, scripts/remediate.sh applies real writes through the Kubernetes API — set image, patch, cordon, drain, create service, apply — and the same scan comes back with 0. Nothing is replayed: the mock reconciles the writes and k8sgpt re-derives the result.
We stood up a mock Kubernetes API server in Python — real REST API, real discovery, real read/write verbs over TLS, serving a deliberately broken payment-prod namespace — and pointed the real binaries at it. K8sGPT runs its analyzers; a small reference agent handles the alert-driven loop that Robusta performs. Explanations go through llm_proxy.py, which bridges k8sgpt's customrest protocol to any OpenAI-compatible backend. Real Robusta itself runs in the separate kind/ path, against a real cluster.
Of the nine products in the brief, only K8sGPT and Robusta are open source and self-hostable. The rest are commercial SaaS (Datadog, PagerDuty, Cleric, Snyk, Torq, Tines, Workato, GitHub Copilot Autofix). They were skipped per the brief's "if not open source, skip" rule.
An SRE agent that runs as a controller inside your cluster. It queries the Kubernetes API, runs structured analyzers in Go, and uses an LLM only for the natural-language explanation — making it deterministic and safe for production.
The real k8sgpt v0.4.36 binary scans the payment-prod namespace over the Kubernetes REST API — served here by scripts/mock_k8s_server.py, a simulator the binary treats like any other kube-apiserver. Every error below comes from a real Go analyzer; the LLM is not involved at this stage.
$ k8sgpt analyze --kubecontext mock-context --no-cache -n payment-prod AI Provider: AI not used; --explain not set 0: Node worker-3() - Error: worker-3 has condition of type DiskPressure, reason KubeletHasNoDiskSpace: kubelet has disk pressure 1: PersistentVolumeClaim payment-prod/payment-data-pvc() - Error: storageclass.storage.k8s.io "standard" not found 2: Service payment-prod/payment-api-svc() - Error: Service has no endpoints, expected label app=payment-api-frontend 3: Ingress payment-prod/payment-ingress() - Error: Ingress uses the ingress class nginx which does not exist. - Error: Ingress uses the service payment-prod/payment-frontend which does not exist. 4: Pod payment-prod/payment-api-7c4f5b-x9qkl(Deployment/payment-api) - Error: the last termination reason is Error container=api pod=payment-api-7c4f5b-x9qkl 5: Pod payment-prod/payment-worker-6d8b2c-p3mnr(Deployment/payment-worker) - Error: the last termination reason is OOMKilled container=worker pod=payment-worker-6d8b2c-p3mnr 6: Deployment payment-prod/payment-api() - Error: Deployment payment-prod/payment-api has 1 replicas but 0 are available with status running 7: Deployment payment-prod/payment-worker() - Error: Deployment payment-prod/payment-worker has 1 replicas but 0 are available with status running
Passing --explain sends each finding to whichever backend you configured through llm_proxy.py — ollama (the credential-free default), openai, openrouter, zai, any custom OpenAI-compatible endpoint, or replay, which answers offline from responses captured earlier in captured/llm_cache.json. The model produces a step-by-step remediation plan; the findings it explains still come from the deterministic analyzers.
{
"provider": "",
"errors": null,
"status": "ProblemDetected",
"results": [
{
"kind": "Deployment",
"name": "payment-prod/payment-api",
"error": [
{
"Text": "Deployment payment-prod/payment-api has 1 replicas but 0 are available with status running",
"KubernetesDoc": "",
"Sensitive": [
{ "Unmasked": "payment-prod", "Masked": "YSNVVSQjbDVwUHM3" },
{ "Unmasked": "payment-api", "Masked": "NCZ5Tn1hK3ZFWzc=" }
]
}
]
},
{
"kind": "Pod",
"name": "payment-prod/payment-worker-6d8b2c-p3mnr",
"error": [
{
"Text": "the last termination reason is OOMKilled container=worker pod=payment-worker-6d8b2c-p3mnr"
}
]
},
// ... 6 more results (Node, PersistentVolumeClaim, Service, Ingress, second Pod, second Deployment)
]
}
A Kubernetes troubleshooting and automation platform. When a Prometheus alert fires, Robusta autonomously investigates — pulls logs, queries cluster state, calls an LLM, and posts a root-cause card to Slack.
Robusta receives the Alertmanager webhook, decides what to investigate, gathers the evidence, asks the LLM, and posts a Slack card with remediation suggestions.
What runs where. The demo on this page does not run Robusta. It runs scripts/alert_triage_agent.py, a ~250-line reference implementation of the same alert → cluster-context → LLM → Slack-card loop, so you can read the whole thing in one sitting. Slack posting is opt-in: nothing leaves your machine unless you set SLACK_WEBHOOK_URL and pass --post-slack. Real Robusta — the actual robusta-dev Helm chart — runs in the separate kind/ path: make kind-up && make kind-broken && make kind-robusta && make kind-fire-alert creates a real kind cluster, deploys genuinely broken workloads, helm-installs Robusta with the Prometheus stack, and fires a real Prometheus alert into it.
alert_triage_agent.py, recorded live. Not Robusta — Robusta's flow.
A captured LLM root-cause analysis, rendered in the Slack card layout the agent emits. Posting it to a real channel is opt-in — this page is showing the local render, and the alert it responds to is the fixture payload below.
The payment-api pod is in a ContainersNotReady state, likely due to a crash loop or failed startup, causing the high 5xx error rate.
False for Ready and ContainersReady since 03:34:02Z.payment-api rev 3.Critical: Payment API is unavailable, with 12.4% of requests failing. All payment processing is likely impacted.
Check the pod's logs for the api container: kubectl logs payment-api-7c4f5b-x9qkl -c api --previous. If the container is crashing, roll back the deployment: kubectl rollout undo deployment/payment-api.
{
"version": "4",
"status": "firing",
"receiver": "robusta",
"commonLabels": {
"alertname": "PaymentAPIHighErrorRate",
"severity": "critical",
"namespace": "payment-prod",
"service": "payment-api"
},
"commonAnnotations": {
"summary": "Payment API error rate above 5% for 5 minutes",
"description": "Payment API is returning 5xx errors to 12.4% of requests. Threshold: 5%. The service has been erroring since the last deployment (payment-api rev 3) 28 minutes ago."
},
"alerts": [
{
"status": "firing",
"labels": {
"alertname": "PaymentAPIHighErrorRate",
"severity": "critical",
"pod": "payment-api-7c4f5b-x9qkl"
},
"startsAt": "2026-08-20T03:42:00Z",
"fingerprint": "f1a2b3c4d5e6f7a8"
}
]
}
Each component is a thin Python service or static asset. Swap any of them for your preferred alternative — a real kube-apiserver, a Slack webhook, your own model host — without touching the others.
A mock kube-apiserver serving a deliberately broken payment-prod namespace. Full discovery plus full read/write verbs (GET/POST/PUT/PATCH/DELETE) across /api/v1, /apis/apps/v1, /apis/networking.k8s.io/v1, … — faithful enough that the real kubectl and k8sgpt can't tell the difference. A reconciler applies writes, so remediation actually changes state. TLS certs are generated on first boot.
your kube-apiserverStructured Go analyzers (Pod, Deployment, Service, Ingress, PVC, Node, ReplicaSet, …). Rules-based and deterministic — returns 8 findings against this cluster with no LLM involved. Add --explain to route each finding to your configured model.
your SRE's first 30 minutesTranslates k8sgpt's customrest request shape ({model, prompt, options.message}) into an OpenAI-compatible /chat/completions call and injects whatever auth the provider needs. Provider-agnostic: ollama (default, credential-free), openai, openrouter, zai, custom, or replay.
any OpenAI-compatible APIWhatever backend you point the proxy at writes the root-cause analyses for the --explain pass and the triage card. No vendor is required. LLM_BACKEND=replay serves previously captured responses from captured/llm_cache.json with no network at all, which is what keeps the recorded demos reproducible.
ollama / openai / openrouter / zai / customA readable reference implementation of Robusta's loop: receives an Alertmanager webhook, gathers cluster context via kubectl, builds an LLM prompt from alert + state, calls the model, renders a Slack-style incident card. Posting to Slack is opt-in via SLACK_WEBHOOK_URL.
real Robusta runs in kind/Single-file dark-themed landing page with linked demo GIFs, JSON outputs, and a Slack-style RCA card. Self-contained — no JS framework, no build step, no external assets except Google Fonts.
your project's README
No score is baked into this page. The suite reports pass / fail / skip per
check — skipping anything whose prerequisite binary or service is missing rather than
pretending it passed — and writes a machine-readable summary to
outputs/uat_results.json. Run it:
./run.sh uat
| Check | What it verifies |
|---|---|
A1 | the mock API answers the Kubernetes discovery endpoints |
A2 | payment-api Pod is in CrashLoopBackOff |
A3 | payment-worker Pod was OOMKilled (exit 137) |
A4 | worker-3 Node reports DiskPressure=True |
A5 | payment-ingress backend Service does not exist |
A6 | payment-data-pvc is Pending with no StorageClass |
A7 | payment-api-svc selector matches no Pod |
B1 | kubectl reads the cluster |
B2 | kubectl writes are accepted (a patch round-trips through the API) |
B3 | k8sgpt finds every broken resource (8 findings, 6 kinds) |
C1 | remediate.sh drives the finding count to zero with real kubectl writes |
C2 | reset restores the broken state |
D1 | the LLM proxy reports its backend honestly (including replay mode) |
D2 | the proxy answers k8sgpt's customrest request shape |
D3 | the replay cache is keyed on the prompt, not the model name |
E1 | no absolute developer paths anywhere in the repo |
E2 | no credentials committed |
E3 | every file referenced by run.sh exists |
kubectl and k8sgpt binaries and the
mock API running; D* needs the LLM proxy. Anything missing is reported as
skip.
Vercel for the landing page · local Python for the full demo stack against the mock API ·
Docker for the containerized version · make kind-* for a real
kind cluster with real Robusta.
| K8sGPT | Robusta | |
|---|---|---|
| License | Apache 2.0 | Apache 2.0 |
| Language | Go | Python |
| Trigger model | Manual / scheduled / controller | Alert-driven (Alertmanager webhook) |
| Output format | Text + JSON (CLI) | Slack card, PagerDuty, MS Teams |
| LLM role | Optional --explain | Always on for alert enrichment |
| Can auto-remediate? | Experimental | Yes, via opt-in playbooks |
| Best for | Cluster-wide audits, CI gates | Single-alert deep dives, on-call rotation |
| Demo in this page | Real binary: 8 findings → 0 after remediation | Reference implementation of the loop (real Robusta runs in kind/) |
Everything above was produced by real binaries — k8sgpt v0.4.36 and
kubectl — talking to a mock Kubernetes API server over the
real Kubernetes REST protocol. The cluster is simulated; the binaries, the API calls, the
writes and the findings are not. Here are the building blocks.
A single-file Python HTTPS server implementing the Kubernetes REST API —
discovery plus GET/POST/PUT/PATCH/DELETE across /api/v1,
/apis/apps/v1, /apis/networking.k8s.io/v1 and friends —
well enough that kubectl and k8sgpt behave exactly as
they would against a kube-apiserver. It serves broken
payment-prod state and reconciles the writes remediation makes.
Self-signed certs are generated on first boot.
The actual k8sgpt v0.4.36 binary from GitHub releases, configured
with --kubeconfig pointing at the mock. The analyzers run
unmodified and return 8 findings. --output json emits the schema
shown above.
k8sgpt's customrest backend POSTs to a single URL with a
non-OpenAI request shape. This proxy translates that shape into a standard
chat-completions call and injects whatever auth the chosen provider needs, so
the same demo works on ollama, openai, openrouter, zai, any custom
OpenAI-compatible endpoint, or the offline replay cache.
No specific model is required and none is bundled. Point
LLM_BACKEND at whatever you have — ollama needs no
credentials at all. Responses captured from earlier runs live in
captured/llm_cache.json; LLM_BACKEND=replay serves
those verbatim with no network, and labels every answer as a replay.
Runs each demo for real in a pty and records the session to an asciinema cast — the commands actually execute, and what you see scrolling past is the binaries' live output rather than a script's idea of it. (Earlier versions of this repo hand-authored the cast files; that is no longer how the GIFs are made.)
The agg static binary from the asciinema project converts each
.cast file to a GIF. Four GIFs total:
scan.gif, explain.gif, remediate.gif,
triage.gif.
agentic-devops-extravaganza/ ├── index.html # this page ├── gifs/ │ ├── scan.gif # Demo 1: cluster triage │ ├── explain.gif # Demo 2: LLM explanations │ ├── remediate.gif # real kubectl fixes, 8 -> 0 │ └── triage.gif # alert -> LLM -> Slack card ├── recordings/ # the asciinema casts behind the GIFs ├── scripts/ │ ├── mock_k8s_server.py # the simulated cluster │ ├── remediate.sh # the real kubectl fixes │ ├── llm_proxy.py # customrest -> any OpenAI-compat API │ ├── alert_triage_agent.py # reference impl of Robusta's loop │ ├── record_demos.py # records the demos in a pty │ └── run_uat.py # the acceptance suite ├── kind/ # real cluster + real Robusta (helm) └── outputs/ ├── k8sgpt_analyze.json # raw JSON from k8sgpt analyze ├── k8sgpt_analyze.txt # raw text from k8sgpt analyze ├── k8sgpt_analyze_after.txt # the same scan after remediation ├── alert_triage.json # what the triage agent produced └── uat_results.json # last acceptance-suite run