BlueByte
Start request repeated too quicklyFixed

systemd: Start request repeated too quickly

By Haneul SeoUpdated September 27, 20265 min

Hi, it's BlueByte. A service that used to restart itself stays down, systemctl status shows it failed, and the journal says Start request repeated too quickly. That line is systemd's start rate limit, not the real failure: the service died several times in a row and systemd stopped retrying. We'll walk through what the limit counts, how to find the crash underneath, the fix per cause, and how to keep it from coming back.

What the journal and systemctl print when the limit trips

Here is the journal from my reproduction on systemd 255 (Ubuntu 24.04), with a unit whose program exits 1 and Restart=on-failure:

myapp.service: Scheduled restart job, restart counter is at 5.
myapp.service: Start request repeated too quickly.
myapp.service: Failed with result 'exit-code'.
Failed to start myapp.service - My App.

The wording from systemctl start depends on the result systemd records. When the result is start-limit-hit, you get this, plus a hint to run reset-failed:

Job for myapp.service failed because start of the service was attempted too often.

In my test on 255 the unit kept its first result, exit-code, so systemctl start said the control process exited with error code instead. Current systemd source records start-limit-hit here, so which line you see depends on your version. Search the journal for repeated too quickly rather than trusting one message.

Why systemd stops retrying

Every unit has a start limit: more than StartLimitBurst= starts within StartLimitIntervalSec= and further starts are refused. Both live in the [Unit] section and default to 10 seconds and 5 starts. The systemd.service manual is explicit that Restart= is subject to the same limit, and RestartSec= defaults to 100 ms. So a program that dies on startup burns five attempts in well under a second, and systemd gives up. That is a guard against a busy loop, not a bug.

The causes fall into three groups:

  • The program itself fails on every start: bad config, missing file, port in use, wrong permissions.
  • The program fails briefly because something it needs isn't ready yet (a database, a mount, the network), and 100 ms spacing never gives it time.
  • Something outside restarts it repeatedly. Every start counts, whether it comes from Restart=, from you, or from a deploy script.

First, find the crash under the rate limit

You don't need to memorize these; three commands tell the causes apart:

systemctl show myapp -p Result -p NRestarts -p RestartUSec -p StartLimitIntervalUSec -p StartLimitBurst
journalctl -u myapp -b --no-pager | grep -v 'Scheduled restart' | tail -n 30
systemctl cat myapp

My test printed NRestarts=5, StartLimitIntervalUSec=10s and StartLimitBurst=5. The journal lines above repeated too quickly are the part that matters: the program's own error, or a status=203/EXEC meaning the ExecStart= binary could not run at all. If there is no crash between the starts, look for whoever keeps restarting it.

Fix it by cause

For a program that always fails, fix the error, then clear the counter and start again. reset-failed only flushes the counter; on its own it changes nothing:

sudo systemctl reset-failed myapp
sudo systemctl start myapp
systemctl is-active myapp
# active

For a dependency that isn't ready yet, space the restarts out with a drop-in instead of editing the vendor unit:

sudo systemctl edit myapp
[Service]
Restart=on-failure
RestartSec=5s

The rule of thumb: if RestartSec × StartLimitBurst is longer than StartLimitIntervalSec, restarts alone cannot trip the limit. When I set RestartSec=3s on the test unit, 14 seconds later it showed NRestarts=4 and SubState=auto-restart — still retrying instead of failed. On systemd 254 and later, RestartSteps= with RestartMaxDelaySec= gives a growing back-off instead of a fixed delay.

For an external restarter, fix the script or timer. Raising StartLimitBurst= only hides it.

A worked example: an API that starts before PostgreSQL

An API unit starts at boot, can't reach PostgreSQL, logs connection refused, and exits. Five restarts 100 ms apart finish before the database accepts connections, and the API sits in failed until someone logs in. The fix has two parts — order it after the database, and give it room to retry:

[Unit]
After=postgresql.service
Wants=postgresql.service
 
[Service]
Restart=on-failure
RestartSec=5s

After the next reboot, systemctl show api -p NRestarts -p ActiveState shows one or two restarts and ActiveState=active. Ordering alone isn't enough when the database takes a while to accept connections, which is why the delay stays.

Check it end to end, then keep it from coming back

Run systemctl is-active myapp, then watch journalctl -u myapp -f through one deliberate systemctl restart myapp. To prevent a repeat, set RestartSec= explicitly on anything with Restart=, declare real dependencies with After=/Wants=, and alert on units in failed state (systemctl --failed). Setting StartLimitIntervalSec=0 disables the limit entirely; a broken program then retries forever at RestartSec pace, so only do it with a sane delay.

How it differs from 203/EXEC and a start timeout

status=203/EXEC means systemd could not execute the ExecStart= program — wrong path, missing execute bit. It is often the crash underneath this error. Job for myapp.service failed because a timeout was exceeded means one start took longer than TimeoutStartSec=: that is one slow start, not many fast ones.

Next time a service stays down, walk these checks back in order: read the journal above repeated too quickly, fix that error, then decide whether the restart spacing needs to change.

Related questions

Does systemctl reset-failed fix the problem?

No. It flushes the start counter and clears the failed state so you can start the unit by hand again. If the program still crashes on start, it will hit the limit again within seconds. Fix the error you find in the journal first, then reset and start.

Should I just set StartLimitIntervalSec=0?

That disables the rate limit, so systemd keeps restarting a broken program forever at the RestartSec pace. With a sensible delay such as 5 seconds that can be acceptable for a service that must come back on its own, but with the 100 ms default it becomes a tight loop that fills the journal. Prefer a longer RestartSec, and on systemd 254 or later consider RestartSteps= with RestartMaxDelaySec=.

Do my own systemctl restart commands count toward the limit?

Yes. The limit counts starts of the unit, whatever triggered them. A deploy script that restarts a service in a loop, or several restarts typed in quick succession, can trip it even when the program never crashes.

I set StartLimitBurst but systemctl show still prints 5.

Check where the setting went. StartLimitIntervalSec= and StartLimitBurst= are documented in the [Unit] section, so put them under [Unit] in your drop-in. Then confirm the merged result with systemctl cat myapp and systemctl show myapp -p StartLimitBurst. If you edited the file by hand instead of using systemctl edit, run systemctl daemon-reload so systemd rereads it.

Can systemd take a bigger action when the limit is hit?

Yes. StartLimitAction= in the [Unit] section takes the same values as the other failure actions, such as reboot or poweroff, and defaults to none. It is meant for units the machine is useless without; for an ordinary service, alerting on systemctl --failed is usually the better response.

References

Haneul Seo

Infrastructure engineer · 10+ years running Linux fleets

More in this category

Too many authentication failuresFixed

SSH: Received disconnect ... Too many authentication failures

Your agent is offering more keys than the server will let you try. Every public key sshd looks at burns one of the MaxAuthTries attempts — six by default, often three on a hardened host — so the right key never gets its turn and the server hangs up before you type anything. IdentitiesOnly=yes with an explicit IdentityFile pins the connection to one key and the attempt count drops to one.

OpenSSH
1722Fixed

Active Directory: replication fails with error 1722, The RPC server is unavailable

RPC reports 1722 (0x6ba, RPC_S_SERVER_UNAVAILABLE) when a lower layer fails to connect, so the real fault is almost never RPC itself — it is DNS, a blocked port, or a host-side setting on one of the two domain controllers. repadmin tells you which partner is failing, dcdiag /test:dns rules out name resolution, and Test-NetConnection plus the dynamic port range settle the firewall question. The most common miss is a rule that allows TCP 135 but not 49152–65535.

Windows Server Active Directory
Windows Server RDSFixed

Windows Server RDS: The remote session was disconnected because there are no Remote Desktop License Servers available to provide a license

The 120-day RD Licensing grace period ended and the session host has no usable license server, so it refuses sessions. GetGracePeriodDays returning DaysLeft 0 and an empty SpecifiedLSList confirm it in seconds. The fix is a real, activated license server with CALs that are new enough for the host — a 2019 CAL cannot serve a 2022 session host — configured through the deployment or the Licensing policies, plus RPC ports open between the two.

Windows Server RDS
0x80070035 / Event ID 31017Workaround

Windows 11: "Your organization's security policies block unauthenticated guest access" when opening a NAS share (0x80070035)

The SMB client on Windows 10 Enterprise/Education/Pro for Workstations, Windows 11 Pro and Windows Server 2019+ refuses guest logons by default, and Windows 11 24H2 Enterprise/Pro/Education also requires SMB signing, which guest sessions can't do. A NAS share that only offers guest access therefore fails with the 'block unauthenticated guest access' dialog, Error code 0x80070035, or System error 3227320323, and Event ID 31017 'Rejected an insecure guest logon' lands in the SmbClient/Security log. The fix Microsoft recommends is a real account on the NAS and signing support in its firmware; Set-SmbClientConfiguration -EnableInsecureGuestLogons $true (plus -RequireSecuritySignature $false on 24H2) is the escape hatch, and it costs you signing and encryption on that client.

Windows (SMB client)
E: Could not get lock /var/lib/dpkg/lock-frontendFixed

Ubuntu/Debian: E: Could not get lock /var/lib/dpkg/lock-frontend — who holds it and how to wait for it

Another package manager, usually Ubuntu's unattended-upgrades fired by a persistent systemd timer at boot, holds the dpkg frontend lock while your apt-get runs. apt-get gives up at once while apt waits because Ubuntu ships binary::apt::DPkg::Lock::Timeout "120" for the apt binary only. Read the PID from the message, let the run finish or pass -o DPkg::Lock::Timeout=<seconds> to apt-get, run dpkg --configure -a only after a genuinely interrupted run, and never delete the lock file: it is an fcntl lock the kernel releases when the holder exits.

APT (Ubuntu/Debian)
xcrun: error: invalid active developer pathFixed

macOS: xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools)

git, make, clang and other /usr/bin developer commands on macOS are shims that hand off to the active developer directory, and xcrun is reporting that the directory xcode-select points at has no tools in it — most often because a major macOS upgrade left /Library/Developer/CommandLineTools empty, or Xcode was moved or deleted. Check xcode-select -p and the package receipt, then reinstall the Command Line Tools with xcode-select --install, or point xcode-select at the Xcode you actually have.

macOS