6.2 Transitions

Every transition peinit performs. Anything not listed here is not performed.

FromToTrigger
InactiveStartingStart command, dependency resolution, or a timer trigger.
InactiveSkippedA condition check failed.
InactiveFailedValidation, assertion, cycle or dependency failure — before any process existed.
StartingActiveReadiness. Simple only: READY=1 received, or the process exists under Readiness=Alive.
StartingCompletedOneshot exited successfully. Without RemainAfterExit it passes through, releasing dependents, then goes Inactive.
StartingSkippedA condition failed after the activation had already entered Starting.
StartingFailedAn assert failed after entering Starting; or a timeout, hook failure, setup failure, or an exit before readiness.
StartingBackoffA startup failure with the restart policy allowing a retry and budget remaining.
StartingStoppingAn explicit stop cancelling an in-progress start. A restart on a Starting service is queued rather than cancelling.
StartingFailedThe shutdown wave SIGKILLed a starting service.
ActiveReloadingExecReload issued, or SIGHUP delivered.
ActiveStoppingExplicit stop, conflict eviction, a bound dependency stopping, or shutdown.
ActiveBackoffA crash, a watchdog timeout, or health check failure, with a restart allowed and budget remaining.
ActiveFailedThe same three, with RestartPolicy=Never or the budget exhausted.
ActiveInactiveA Simple service exited successfully and RestartPolicy is not Always. Cause CleanExit.
ActiveBackoffA Simple service exited successfully and RestartPolicy=Always. Cause CleanExitRestart.
ReloadingActiveReload resolved: READY=1, the detection window expiring, the extended wait expiring, or the reload command exiting — success or failure.
ReloadingStoppingThe same triggers as Active to Stopping. The reload is cancelled.
ReloadingBackoffThe main process exited during the reload, with a restart allowed.
ReloadingFailedThe same, with no restart available.
StoppingInactiveThe process exited after an explicit stop or a shutdown.
StoppingFailedThe process exited after a conflict eviction or a bound dependency stopping.
StoppingAbandonedSIGKILL sent and main/ still populated after the post-kill deadline.
StoppingStartingAn explicit start queued against a service that is still stopping. Cause ExplicitStart.
BackoffStartingThe backoff delay elapsed. Cause RestartPolicy.
BackoffInactiveAn explicit stop cancelled the pending restart.
BackoffSkippedThe backoff delay elapsed and the relaunch's pre-start checks skipped the service: its terminal is now held (§11.6), or a condition no longer holds.
BackoffFailedThe backoff delay elapsed and peinit could not execute the relaunch. Cause InternalError.
Starting, Active, Reloading, StoppingFailedpeinit could not carry out a step on the service and contained the error to it (§8.2). Cause InternalError.
CompletedInactiveRemainAfterExit=0 and dependents released; or an explicit stop; or shutdown clearing it.
CompletedStartingA start command or timer trigger re-running the Oneshot.
FailedStartingAn explicit start, a bound dependency recovering, or a timer trigger.
FailedInactiveA reset command clearing the state.
FailedAbandonedA service SIGKILLed by the shutdown wave whose cgroup stayed populated past the post-kill deadline.
AbandonedInactiveA reset command.
SkippedInactiveA reset command, or an explicit start clearing Skipped before it re-evaluates the conditions.

6.2.1 Things the table settles #

A restart never passes through Failed. A restart-eligible failure goes to Backoff, waits, and goes to Starting. Failed is reached only when there will be no retry: RestartPolicy=Never, an invalid policy for the cause, or an exhausted budget. This is why OnFailure (§6.3), which fires on entry to Failed, does not fire on each retry — only when the service finally fails out.

A clean exit is not a crash. A Simple service exiting zero goes to Inactive under cause CleanExit, consulting neither the restart policy nor the budget. It goes to Backoff only under RestartPolicy=Always, and then with the distinct cause CleanExitRestart, so status and events say plainly that the process succeeded and was restarted by policy rather than that anything went wrong.

A forced stop remembers why. Stopping to Failed carries the cause from the transition that started the stop — ConflictEviction or BindsToPropagation — rather than a generic failure. A service that lost a conflict and a service whose binding target went away are distinguishable afterwards, which is what makes bound-dependency recovery (§7.1) possible at all.

A restart detours through Inactive. The stop leg of an administrator's restart ends in Inactive, and the start leg begins from there. The two legs meet within one turn of the event loop, and peinit answers no request mid-turn, so no query ever sees the service Inactive in between.

A crash before readiness may be retried. A Simple process that exits before signalling readiness is a ProcessCrash from Starting, and is restart-eligible like any other, rather than a terminal startup failure.

A process can outlive its service's state. Most states are reached by the main process exiting, but not all of them: a watchdog timeout, a health check escalation and the post-kill give-up all move a service on while its process is still running. The exit that follows arrives for a service in Backoff, Abandoned or another state that no longer expects one.

peinit records that exit and performs no transition. The service keeps the state it had, any pending restart deadline stands, and the console reports it:

peinit: service <service> main process exited in state <state>; no action taken

This is deliberately not a fatal condition. A supervised service producing an unexpected sequence costs that service; it does not cost the machine. Treating it as a broken invariant of the runtime loop meant one crash-looping service dropped PID 1 into recovery mode and killed every session on the box.

6.2.2 Reset from Abandoned #

Resetting an Abandoned service re-checks its main/ sub-cgroup. If it has finally emptied, peinit cleans up the whole service tree — main/, hooks/, health/, then the root — and transitions to Inactive.

If it is still populated, peinit leaves the cgroup leaked, transitions to Inactive anyway, and returns this warning in the operation acknowledgement:

abandoned main cgroup for service <service> is still populated after
reset -- cgroup remains leaked; underlying D-state process requires
investigation

The warning is also written to the console, so a reset issued without reading the response still leaves a trace of the still-leaked cgroup.

The re-check targets main/. Note that the two paths into Abandoned probe different cgroups: an explicit stop checks main/, while the shutdown wave checks the service root.

Edit this page