Fixing 'start-limit-hit' Errors in systemd
systemctl reports start-limit-hit when systemd's crash-loop protection trips — clearing the rate limit is a distinct step after fixing the real cause.
systemctl start myservice reports Failed to start myservice.service: Unit myservice.service not found — no wait, more precisely: “start request repeated too quickly” or “start-limit-hit” means systemd has deliberately stopped trying to start a unit that’s failed too many times in a short window.
Step 1: check the unit’s actual failure reason first
systemctl status myservice
journalctl -u myservice -n 50
The rate limit is systemd protecting against a genuine crash loop — fixing why the service keeps failing is the real work; clearing the rate limit alone just lets it fail rapidly again.
Step 2: identify and fix the actual underlying problem
Common causes behind the repeated failures: a missing configuration file, an unavailable dependency (a database the service can’t reach yet), incorrect permissions, or a bug triggered on every startup — the journal output from Step 1 should point at one of these directly.
Step 3: check the unit’s own rate-limit configuration
[Unit]
StartLimitIntervalSec=60
StartLimitBurst=3
This unit fails permanently after 3 restart attempts within 60 seconds — worth knowing these values exist and are adjustable, though the fix is rarely “just allow more retries.”
Step 4: reset the failed state once the real cause is fixed
systemctl reset-failed myservice
This clears systemd’s internal failure counter for the unit — required after fixing the underlying issue, since systemd won’t attempt a fresh start cycle until this counter is cleared.
Step 5: start the service again
systemctl start myservice
Step 6: watch it through a few natural restart cycles if it’s meant to restart on its own
journalctl -u myservice -f
For a service configured with Restart=on-failure, confirm it now survives whatever transient condition (a slow-starting dependency) previously triggered the loop, rather than hitting the same limit again after the reset.
Step 7: add an explicit dependency if the real cause was a race at boot
[Unit]
After=postgresql.service
Requires=postgresql.service
A service crash-looping specifically at boot, because a dependency (a database, a network mount) isn’t ready yet, is fixed properly by declaring that dependency explicitly — not by increasing the restart burst limit to outlast the race.
Why reset-failed is easy to forget
Fixing the root cause and restarting the service manually often works once — but systemd’s own internal failure counter persists across that manual start, meaning the very next automatic restart attempt (from a boot, or a monitoring system triggering a restart) can immediately hit the same limit again if it was never cleared. Making reset-failed a standard part of the fix, not an optional afterthought, avoids the confusing “I fixed it and it broke again” experience.
Step 8: check whether StartLimitAction makes this more urgent than it looks
[Unit]
StartLimitAction=reboot
By default, exhausting a unit’s start limit simply leaves it failed and inert — but StartLimitAction can be configured to trigger reboot, reboot-force, or poweroff instead, intended for units where “just stay down” isn’t an acceptable failure mode. A service crash-looping at boot with this option set can put a machine into a genuine reboot loop, restarting, hitting the same start limit, and rebooting again indefinitely — which turns diagnosing the underlying failure (Step 1-2) into a race against the next automatic reboot, and is worth checking for immediately with systemctl cat myservice if a system is rebooting repeatedly rather than simply failing a service and stopping.
Step 9: watch restart counts and timestamps directly rather than inferring them
systemctl show myservice -p NRestarts,ActiveEnterTimestamp,InactiveEnterTimestamp
Rather than reconstructing how many times a unit has restarted and when from journal timestamps by hand, systemctl show with these specific properties reports systemd’s own internal counters and state-transition times directly — useful both for confirming a fix actually stopped the crash loop (NRestarts staying flat after the fix, rather than continuing to climb) and for establishing exactly when the failing pattern first began relative to some other change on the system.
Related:
- Understanding systemd: Units, Targets, and the Modern Linux Init System
- Fixing ‘Too Many Open Files’ Errors on Linux
Sources: