A scrape target disappears. Prometheus has not heard from payment-service for thirty seconds and the page fires. This is the alert where minutes cost the most — and, awkwardly, the one where the obvious remediation is the thing an agent should least be trusted to do alone.

Three very different incidents, one alert

Before anything else: up == 0 does not mean the service is down. It means Prometheus could not reach it, which is a weaker claim than it looks.

What is trueHow it looksUrgency
The service is genuinely downNo pods ready, or all crash-loopingHighest. Customers are affected now.
The service is fine, monitoring cannot reach itPods ready, traffic still being served, error rate normalLow. It is a monitoring incident wearing a service-down costume.
Mid-deploymentOld pods terminating, new ones not ready yetNone, usually. It resolves in ninety seconds.

The second and third rows are common enough that paging a human immediately on up == 0 is how teams train themselves to ignore the alert — which is the worst possible outcome for the row that matters.

The runbook

Service Down Matches: alert_name ~ ServiceDown · severity in high, critical
1
Pod statusk8s.pods→ saves as pods

Ready replicas, restart counts, and whether anything is in CrashLoopBackOff. Separates row one from rows two and three immediately.

2
Recent eventsk8s.events→ saves as events

OOMKilled, FailedScheduling, image pull errors, or a rollout in progress. This is where the cause usually names itself.

3
Decide: safe to restart?ai.decide→ saves as restart_ok

Weighs pods and events against one question: would a restart plausibly recover this without losing anything? A stateful service mid-write is a different answer from a stateless one that OOMed.

4
Approve restarthuman.approve

The execution parks here. run_if {{restart_ok}} == 'yes' — the agent only asks when it has a reason to.

5
Restartbash.exec

Runs only after a named person approves. kubectl -n {{namespace}} rollout restart deploy/{{service_name}}

Why the approval gate is here and not elsewhere

This is the runbook where the argument for full autonomy is strongest — the service is down, every minute counts, and a restart fixes it most of the time. We still gate it, and the reasoning is worth setting out because it is not squeamishness.

graph TB Q["Should the agent restart
without asking?"] --> A{"How long before
we notice it was wrong?"} A -->|"immediately — it either
comes back or does not"| B{"What does being
wrong cost?"} B -->|"a few more seconds
of downtime"| C["Arguably safe"] B -->|"an in-flight write lost,
a queue re-consumed,
a stuck lock never diagnosed"| D["Gate it"] A -->|"not until much later"| D style D fill:#fde8e8,stroke:#e57373,stroke-width:2px

The deciding factor is the third branch. A restart does not merely fail to fix the wrong cases — it destroys the evidence. The pod that was about to be inspected, the thread dump nobody took, the stuck connection that would have named the deadlock: all gone, and the incident recurs in two hours with less to go on than the first time.

The real cost of an automatic restart

It is not the risk that the restart breaks something. It is that a restart converts a diagnosable incident into a recurring mystery. Teams that automate restarts on this alert frequently end up with a service that "just needs restarting every few days" and nobody who can say why.

What the approval looks like in practice

The point of the gate is not to slow things down. It is to put the decision in front of a person who is already holding a complete picture, so answering takes seconds rather than twenty minutes.

sequenceDiagram autonumber participant P as Prometheus participant IR as InsightResolve participant Eng as On-call P->>IR: ServiceDown · payment-service Note over IR: 0–40s: pods, events, decision IR->>Eng: incident with evidence + "Restart payment-service in prod?" Note over Eng: reads: 0/3 ready, OOMKilled ×4,
no deploy in the window Eng->>IR: Approve — "memory limit, restart then raise it" IR->>IR: resumes within seconds, runs the restart Note over IR: the answer, who gave it and the
note are on the step permanently

Compare that with the unautomated version: the engineer wakes, connects, finds the namespace, runs kubectl get pods, runs kubectl describe, reads the events, then decides. The decision is the same. The twelve minutes in front of it are gone.

And if nobody answers, the timeout escalates. The one outcome never permitted is an incident sitting in waiting because everyone assumed someone else had it.

The step most teams should add

The seeded runbook does not include it because the demo stack has no traffic source, but for a real estate it is the most valuable step in this runbook: check whether the service is actually serving traffic.

A prometheus.instant on request rate, or an elb.target_health against the load balancer, answers row two of the table at the top directly. If the pods are ready, the targets are healthy and requests are being served at the normal rate, then Prometheus has a scrape problem and nobody needs to be woken up. That single step converts a meaningful share of these pages from an escalation into a note.

The ordering that matters

Put the traffic check before the restart decision, not after. An agent that has already decided a restart is warranted and then discovers traffic is flowing has wasted the reasoning; one that checks first often never asks the question.

Adapting it beyond Kubernetes

  • ECS: ecs.services gives desired versus running counts, rollout state and the last five service events — the same information as pods plus events, in one step.
  • Behind a load balancer: elb.target_health is often a better first signal than the scrape, because it reflects what the traffic path thinks rather than what the monitoring path thinks.
  • Cloud-managed services: add cloud.health early. "Is it us or the provider?" is a one-step question and occasionally the entire answer.
  • Anywhere: keep the approval gate on the remediation. The platform changes; the argument for a person owning the restart does not.

Next in this series

A data pipeline failed at 2am — a different shape of incident entirely: nobody is being paged about a customer, but the morning report is at risk.

Keep reading