Restarts and Failures
Scope: how systemd decides a service "started," what it does when one dies, and where it gives up — with a real crash loop dissected.
Type=: what "started" means
Type tells systemd how to know the service is up:
| Type | systemd considers it started when... | Use for |
|---|---|---|
simple |
the process is running | almost everything; the default |
oneshot |
the command finishes (exit 0 = success) | batch jobs, setup steps — timers' partner |
forking |
the process forks and the parent exits | legacy daemons that insist on self-backgrounding |
notify |
the program sends an sd_notify signal | programs with real readiness signaling |
Two mortal traps: a forking-style script under
Type=simple that backgrounds itself and exits — systemd
sees the main process exit and concludes the service
stopped, then cleans up the actual worker. And a
oneshot that never exits — the unit sits in
activating forever and anything ordered after it
waits. Match the type to the program's actual shape; the
demo and most modern programs are simple.
Restart=: what happens when it dies
| Policy | Restarts when |
|---|---|
no (default) |
never |
on-failure |
nonzero exit, signal death (kill, segfault), timeout, watchdog |
on-success |
only on clean exit 0 |
always |
any exit at all — even a deliberate stop is honored only via systemctl stop, which overrides |
on-abnormal / on-abort |
signals/timeouts only, not nonzero exits |
RestartSec= is the pause before each attempt (the demo
used 2s). A clean exit under on-failure is respected
— the demo's SIGTERM trap exiting 0 is why a deliberate
stop doesn't fight the restart policy.
The crash loop, and giving up
examples/mere-flaky.service
runs a script that exits 1 immediately, with
Restart=on-failure, RestartSec=1. Watched live:
systemd[1]: mere-flaky.service: Scheduled restart job, restart counter is at 5.
systemd[1]: mere-flaky.service: Start request repeated too quickly.
systemd[1]: mere-flaky.service: Failed with result 'exit-code'.
and final status:
× mere-flaky.service - Crashes immediately, restarts until systemd gives up
Active: failed (Result: exit-code) since ...
Process: 16440 ExecStart=/usr/local/bin/mere-flaky.sh (code=exited, status=1/FAILURE)
systemd's patience is a start limit: by default, 5
starts within 10 seconds (StartLimitBurst,
StartLimitIntervalSec — settable per unit in [Unit])
and it stops trying, leaving the unit failed. This is
deliberate protection: a broken service shouldn't burn a
machine forever, and a human should look. After fixing
the cause:
sudo systemctl reset-failed mere-flaky.service
sudo systemctl start mere-flaky.service
reset-failed clears the failed state and the restart
counters. Restarting without it fails instantly with
the same "repeated too quickly" story — a classic
ten-minute confusion the first time you meet it.
Exit statuses you'll actually see
In status, under Process: / Main PID::
status=0/SUCCESS— clean.status=1/FAILURE(or any small number) — the program exited with that code. Your bug, or your program honestly reporting failure.status=203/EXEC— the executable couldn't be started at all: path wrong, not executable, bad shebang. systemd's own error range (200s) means "my side of the fence" — see Troubleshooting.code=killed, signal=KILL/SEGV— something shot it: OOM killer (checkjournalctl -b -1for "Out of memory" lines) or a segfault.