A Runbook Needs a Stop Condition

Published · System Administration

A runbook can be beautifully formatted and still be dangerous. The happy path has six numbered steps, each command is easy to copy, and the final instruction says to confirm that the service is healthy. What happens when step three returns a result nobody expected?

If the answer is “use your judgment,” the document has quietly handed its hardest decision to the person least likely to have the missing context. During an incident, that person may be tired, unfamiliar with the service, and under pressure to make the alarm go away.

A useful runbook tells an operator when its instructions still apply, when to stop making changes, and how to hand off the situation. Stopping at a defined boundary is successful execution of the procedure. It should not require an argument about whether you tried hard enough.

State the conditions under which the procedure is valid

Start with the operation's purpose and its permitted scope. “Restart a worker” is too loose. “Drain and replace one unhealthy worker in this pool while the remaining workers have verified spare capacity” gives the operator something to check.

Write preconditions that can be observed before the first mutation:

  • The account, environment, region, and resource identity match the request.
  • The service is in a state this procedure supports.
  • The operator has the required permissions and an incident or change reference.
  • Required dashboards and control-plane status are available and current.
  • Another responder is not already changing the same resource.
  • The service owner has defined the capacity and health limits for this operation.

These are inputs to the decision, not introductory housekeeping. If you cannot establish resource identity, do not turn “probably production-west” into permission to execute. If the health dashboard is stale, the procedure cannot rely on it as evidence of spare capacity.

AWS's runbook guidance calls for prerequisites, constrained targets, expected outcomes, success verification, and escalation conditions. The practical extension is to make those checks executable by the person holding the pager: name the evidence and the result that permits continuation.

Put the stop condition beside the action

An appendix called “Troubleshooting” is a poor place to hide the instruction that prevents step four from making step three worse. Put each boundary directly beside its action.

A step needs four things: what to do, what you expect to observe, how long you will observe it, and what happens if the result differs. “Wait until healthy” leaves the last two undefined.

Observation Operator response
Expected result within the approved window Record evidence and continue
Documented transient failure with a permitted retry Retry within the stated budget
Unexpected state or missing evidence Stop further mutations and hand off
Harm indicator crosses its defined limit Invoke the specified incident or containment path

Keep success and harm signals separate. A drain request can be accepted while queue age increases. A command can return zero while the business operation fails. Your stop rule needs to cover the service effect as well as the tool's return value.

A worked example: draining one queue worker

Consider a hypothetical queue service. Workers receive leased jobs; the supported drain operation prevents new assignments while allowing in-flight jobs to finish. The team's documented replacement procedure permits replacing one worker at a time. Its service owner has already approved an observation window of two minutes and specific queue-age and error-rate limits.

Those details are assumptions for this example, not portable defaults. A different lease model or job duration needs a different procedure.

Before starting, the operator records the worker ID, current lease count, pool capacity, queue-age graph, and incident reference. They verify that another worker replacement is not in progress. They then request the drain once and save the operation ID.

The next step is observation. The expected result is that the worker's active lease count falls to zero within two minutes, the drain state becomes complete, and the pool remains within its approved health limits. Only that combination permits replacement.

Suppose the control plane reports “draining,” but the lease count remains unchanged at the end of the window. The operator does not issue a second drain request, terminate the worker, or invent a lease-release command. They stop further mutations, retain the operation ID, capture the last observed state, and contact the queue-service on-call.

This is where vague procedures often fail. “Drain timed out, try restarting” sounds decisive but changes the operation's assumptions. What if a job is still doing work? What if the drain request succeeded but the status path is delayed? The runbook needs a separate, authorized branch before that next action becomes reasonable.

Stopping also does not mean walking away. The operator continues the specified read-only monitoring and communicates that the worker remains draining. If the queue-age limit is crossed, they use the named incident escalation path immediately rather than waiting for a routine reply.

Bound retries by safety, attempts, and elapsed time

A retry is appropriate only when the procedure explains why repetition is safe for that action and failure mode. Read-only polling and an operation that creates a replacement resource deserve different treatment.

Distinguish repeating an observation from repeating a mutation. Polling the status of an existing operation does not justify submitting it again. An ambiguous timeout after submission should prompt reconciliation using the operation ID or another supported identity mechanism.

For each permitted retry, specify:

  • The exact failure or response that qualifies.
  • Whether the action is idempotent or requires an idempotency token.
  • The maximum total attempts, including the first attempt.
  • The delay and total elapsed-time ceiling.
  • The evidence to check before trying again.

AWS Systems Manager Automation's action reference exposes controls such as maxAttempts, timeoutSeconds, and onFailure; failed steps abort by default. It also explains that retries affect when a step is finally treated as timed out. Review the combined timing behavior rather than assuming one timeout bounds the entire workflow.

A CI Retry Needs a Failure Policy applies the same discipline to a different system: name which failure repetition can address before granting another attempt.

Define what stopping leaves behind

“Abort” can describe the workflow engine while saying very little about the resource it changed. A stopped procedure might leave a worker draining, a deployment paused, or a temporary maintenance setting enabled.

Document that residual state. Identify any containment action the operator may take, any cleanup they must avoid, and who owns the next decision. If cleanup itself carries risk, give it its own preconditions and verification rather than smuggling it into a failure handler.

Rollback belongs here only when the current state satisfies the rollback procedure's assumptions. A Rollback Plan Needs a Compatibility Check explains why returning to an old release is a separate compatibility question.

The operator should be able to report: “I stopped at this boundary; these changes have happened; this state remains; this owner is taking over.” That is much more useful than “the script failed.”

Make the handoff small enough to use

The escalation route needs a role, a contact mechanism, and a fallback if the first contact does not respond. Avoid a person's name as the only durable path.

Hand off the resource and environment, operation ID, last completed step, actual result, expected result, timestamps, remaining state, and relevant evidence. Include what you have deliberately not attempted. Keep credentials and customer-sensitive payloads out of broadly visible incident notes.

When Should a Staff Engineer Escalate a Technical Decision? addresses who can accept a technical tradeoff. Here the immediate question is narrower: the active procedure no longer explains the system in front of the operator. Stop its mutations and transfer the evidence to someone authorized to choose the next path.

A reusable runbook template

Use this as a review skeleton, replacing every placeholder with service-specific evidence:

Purpose and supported scenarios:
Owner / last verified date:
Target identity and authorization:
Preconditions and evidence sources:
Conflicting operations to exclude:

For each step:
  Action and permitted scope:
  Expected observable result:
  Observation window:
  Harm indicators and limits:
  Safe retry conditions / total attempts / elapsed ceiling:
  Stop condition:
  Residual state and permitted containment:

Success verification:
Escalation role / contact / no-response fallback:
Handoff evidence and sensitive-data exclusions:
Separate recovery procedure and its preconditions:

Review the document with a responder who did not write it. Give them a stale dashboard, an ambiguous timeout, an incorrect target, and a missing contact. Can they identify the stop point without guessing? In a controlled test environment, also verify that automation stops before the next mutation and preserves enough evidence to explain why.

Reward the correct stop

After a runbook is used, review where its model matched reality and where it did not. Update conditions and evidence sources; do not simply add another command to the bottom.

The strongest operational culture makes a defined stop easy to defend. A responder who follows the boundary and supplies a clear handoff has done useful work, even while the incident continues. That is how a runbook reduces risk instead of turning pressure into a longer sequence of guesses.

Find more practical engineering and operations guidance at Slaptijack.

Slaptijack's Koding Kraken