The Restart Was Not the Recovery

A restart is satisfying because it produces motion. The process stops, the process starts, and the terminal returns a clean status. That can be exactly the right repair. It can also leave the system holding the same bad state, an unprocessed queue, and several users who are still refreshing a bro...

Hands wiring electronics with tools and laptop nearby

A restart is satisfying because it produces motion. The process stops, the process starts, and the terminal returns a clean status. That can be exactly the right repair. It can also leave the system holding the same bad state, an unprocessed queue, and several users who are still refreshing a broken page.

My runbooks used to give the restart too much ceremony and the recovery too little. They described the command carefully, then ended with some version of “confirm the service is up.” Up meant a process existed. Recovery asks a harder question: has the system returned to a state where its unfinished work, dependencies, and users are being handled correctly?

The distinction changes every step around the command.

Record the state before touching it

The first runbook section isn't a restart instruction. It's a short preservation step. I want the current error, the first known symptom, the affected function, and the state of any pending work. If there’s a queue, I record its depth and the age of its oldest item. If the application writes durable jobs, I check whether they’re waiting, active, or in an uncertain middle state.

This isn't an invitation to spend an hour collecting logs while the system burns. It’s a small evidence budget. Once a process restarts, volatile clues disappear, timestamps shift, and a stuck task may be presented as newly discovered. Five deliberate observations are usually more valuable than five thousand lines copied without a question.

The runbook also says what not to disturb. A restart of an application process shouldn’t casually become a restart of its database, proxy, and host. Broad resets erase the differences that help locate a fault. They can also turn one recoverable problem into several simultaneous startup problems, which is a fairly expensive way to make the screen look busy.

Restart the smallest relevant unit

The command belongs here, after the boundary is named. The unit might be one worker, one application service, or one host process. The choice follows the symptom and the dependency map, not whichever command is easiest to remember.

Before running it, I check what the stop signal does. Does the worker finish its current item? Does it return that item to a queue? Can another worker claim the same item while shutdown is in progress? “Restart safe” is a property of the surrounding state transition, not of restart as a verb.

I also want the expected startup sequence in plain language. Configuration loads, durable state opens, dependencies connect, workers begin accepting work. If the order matters, the runbook says so. Otherwise a process can be technically alive while spending its first minutes failing against a dependency that wasn't ready. That’s not recovery. That’s a fresh failure with a younger PID.

Reconcile work that crossed the restart

After startup, the runbook returns to the work recorded at the beginning. This is the part I used to skip.

For each item that was active or pending, I need to know which of four things happened: it completed before shutdown, it remained safely pending, it was retried, or its outcome is unknown. The unknown case can't be rounded into failure and blindly replayed. If the work sends a notification, writes an external record, or performs another non-repeatable action, retrying may duplicate the effect.

The safest systems make these states visible. Where they don't, the runbook has to provide a read-back method. Check the destination, inspect the durable job record, or compare the last committed position with the next eligible item. A clean worker log can't settle an effect that happened outside the worker.

Backlog behavior matters too. Restarting several consumers at once may drain a queue quickly, or it may overwhelm the dependency that contributed to the problem. The runbook specifies a controlled return: enable a limited amount of consumption, watch completion and error behavior, then increase only if the system is actually clearing work. I don't need an elaborate formula. I need to avoid celebrating a falling queue while failures pile up somewhere else.

Check the function, not the pulse

A process check still belongs in the runbook. It’s just the first post-restart check. I confirm the service stayed running long enough to get past initialization, then exercise the affected function with a safe representative operation.

That might mean submitting a disposable internal job and confirming its durable result, or reading a known non-sensitive object through the normal application path. The check should touch the dependency and state transition involved in the symptom. A generic home page can succeed while background processing remains wedged. A port can accept connections while every useful request fails after authentication.

I compare this evidence with the pre-restart snapshot. Is the old work moving? Are new items completing? Has error behavior changed rather than merely paused? If the answer is unclear, the system is still in recovery. The runbook doesn't allow a green process status to close the procedure on its own.

End with a stable interval

The final step is deliberately uneventful: wait through a meaningful operating interval and inspect again. Immediate success proves startup. A stable interval gives caches time to refill, scheduled work time to run, and the backlog time to reveal whether it’s draining or cycling.

Closure records what recovered, what remains, and what evidence supports the decision. Any held or uncertain work gets an explicit disposition. Temporary logging or reduced concurrency gets either removed or assigned a follow-up. If the restart only bought time, I say that. A workaround can be useful without receiving a promotion to root cause.

I still restart services. Sometimes it’s the fastest clean correction available. I just don't let the command write the ending anymore. The runbook ends when the interrupted work is accounted for, a real function succeeds, and the system keeps doing useful work after the excitement of startup has passed.