systemd: Start request repeated too quickly
안녕하세요, BlueByte입니다. 스스로 재시작하던 서비스가 내려간 채로 남고, systemctl status에는 failed가, 저널에는 Start request repeated too quickly가 찍히는 경우가 있습니다. 이 줄은 systemd의 시작 속도 제한이지 진짜 실패가 아닙니다. 서비스가 연달아 여러 번 죽었고 systemd가 재시도를 멈춘 것입니다. 오늘은 이 제한이 무엇을 세는지, 그 아래 숨은 크래시를 찾는 법, 원인별 해결, 그리고 예방까지 하나씩 짚어보겠습니다.
제한에 걸렸을 때 저널과 systemctl이 보여주는 것
systemd 255(Ubuntu 24.04)에서 프로그램이 1로 종료하고 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.systemctl start의 문구는 systemd가 기록한 결과(result)에 따라 달라집니다. 결과가 start-limit-hit이면 다음 문구와 함께 reset-failed를 쓰라는 안내가 나옵니다:
Job for myapp.service failed because start of the service was attempted too often.제 255 테스트에서는 유닛이 처음 결과인 exit-code를 유지해서 systemctl start가 대신 the control process exited with error code라고 했습니다. 현재 systemd 소스는 여기서 start-limit-hit을 기록하므로, 어떤 문구가 보일지는 버전에 따라 다릅니다. 메시지 하나를 믿기보다 저널에서 repeated too quickly를 검색하세요.
systemd가 재시도를 멈추는 이유
모든 유닛에는 시작 제한이 있습니다. StartLimitIntervalSec= 안에 StartLimitBurst=번보다 많이 시작하면 이후 시작은 거부됩니다. 둘 다 [Unit] 섹션에 두며 기본값은 10초와 5회입니다. systemd.service 매뉴얼은 Restart=도 같은 제한을 받는다고 명시하고, RestartSec=의 기본값은 100ms입니다. 그래서 시작하자마자 죽는 프로그램은 1초도 안 되어 다섯 번을 소진하고, systemd는 포기합니다. 무한 루프를 막는 장치이지 버그가 아닙니다.
원인은 세 갈래입니다:
- 프로그램 자체가 매번 실패합니다: 잘못된 설정, 없는 파일, 사용 중인 포트, 틀린 권한.
- 필요한 것(데이터베이스, 마운트, 네트워크)이 아직 준비되지 않아 잠깐 실패하는데, 100ms 간격으로는 준비될 틈이 없습니다.
- 외부의 무언가가 계속 재시작합니다.
Restart=든, 사람이든, 배포 스크립트든 시작은 모두 셉니다.
먼저 속도 제한 아래의 크래시 찾기
외울 필요는 없습니다. 세 명령이면 원인이 갈립니다:
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제 테스트는 NRestarts=5, StartLimitIntervalUSec=10s, StartLimitBurst=5를 출력했습니다. 중요한 건 repeated too quickly 위쪽 저널 줄입니다. 프로그램 자체의 오류이거나, ExecStart= 바이너리를 아예 실행하지 못했다는 status=203/EXEC입니다. 시작 사이에 크래시가 없다면 누가 계속 재시작하는지 찾아보세요.
원인별 해결
매번 실패하는 프로그램이라면 오류를 고친 뒤 카운터를 비우고 다시 시작합니다. reset-failed는 카운터만 비울 뿐 그것만으로는 아무것도 바꾸지 않습니다:
sudo systemctl reset-failed myapp
sudo systemctl start myapp
systemctl is-active myapp
# active아직 준비되지 않은 의존성이 원인이라면 벤더 유닛을 고치지 말고 drop-in으로 재시작 간격을 벌립니다:
sudo systemctl edit myapp[Service]
Restart=on-failure
RestartSec=5s어림 규칙은 이렇습니다. RestartSec × StartLimitBurst가 StartLimitIntervalSec보다 길면 재시작만으로는 제한에 걸릴 수 없습니다. 테스트 유닛에 RestartSec=3s를 주었더니 14초 뒤에도 NRestarts=4, SubState=auto-restart로 — failed가 아니라 계속 재시도 중이었습니다. systemd 254 이상에서는 RestartSteps=와 RestartMaxDelaySec=로 고정 지연 대신 점점 늘어나는 back-off를 줄 수 있습니다.
외부 재시작이 원인이라면 스크립트나 타이머를 고치세요. StartLimitBurst=를 올리면 가려질 뿐입니다.
따라 해 보기: PostgreSQL보다 먼저 뜨는 API
API 유닛이 부팅 때 시작해 PostgreSQL에 닿지 못하고 connection refused를 남기고 종료합니다. 100ms 간격 재시작 다섯 번이 데이터베이스가 연결을 받기 전에 끝나 버리고, API는 누군가 로그인할 때까지 failed로 남습니다. 해결은 두 부분입니다. 데이터베이스 뒤로 순서를 잡고, 재시도할 여유를 줍니다:
[Unit]
After=postgresql.service
Wants=postgresql.service
[Service]
Restart=on-failure
RestartSec=5s다음 재부팅 후 systemctl show api -p NRestarts -p ActiveState는 재시작 한두 번과 ActiveState=active를 보여줍니다. 데이터베이스가 연결을 받기까지 시간이 걸리면 순서만으로는 부족하기 때문에 지연도 남겨 둡니다.
끝까지 확인하고 재발 막기
systemctl is-active myapp를 실행한 다음, 일부러 systemctl restart myapp를 한 번 하면서 journalctl -u myapp -f를 지켜보세요. 재발을 막으려면 Restart=가 있는 유닛에는 RestartSec=를 명시하고, 실제 의존성은 After=/Wants=로 선언하고, failed 상태 유닛(systemctl --failed)에 알림을 거세요. StartLimitIntervalSec=0은 제한을 완전히 끕니다. 그러면 고장 난 프로그램이 RestartSec 간격으로 영원히 재시도하니, 적당한 지연과 함께만 쓰세요.
203/EXEC, 시작 타임아웃과 다른 점
status=203/EXEC는 systemd가 ExecStart= 프로그램을 실행하지 못했다는 뜻입니다 — 틀린 경로, 실행 비트 누락. 이 오류 아래 숨은 크래시인 경우가 많습니다. Job for myapp.service failed because a timeout was exceeded는 한 번의 시작이 TimeoutStartSec=보다 오래 걸렸다는 뜻입니다. 빠른 시작 여러 번이 아니라 느린 시작 한 번입니다.
다음에 서비스가 내려간 채로 남으면 이 순서를 거꾸로 따라가 보세요. repeated too quickly 위쪽 저널을 읽고, 그 오류를 고친 다음, 재시작 간격을 바꿔야 할지 판단하세요.
관련 질문
systemctl reset-failed로 해결되나요?
아닙니다. 시작 카운터를 비우고 failed 상태를 지워서 유닛을 다시 수동으로 시작할 수 있게 할 뿐입니다. 프로그램이 여전히 시작하자마자 죽는다면 몇 초 안에 다시 제한에 걸립니다. 저널에서 찾은 오류를 먼저 고친 뒤 reset하고 시작하세요.
그냥 StartLimitIntervalSec=0으로 두면 안 되나요?
그러면 속도 제한이 꺼져서 systemd가 고장 난 프로그램을 RestartSec 간격으로 영원히 재시작합니다. 5초 같은 적당한 지연이라면 스스로 복구돼야 하는 서비스에 괜찮을 수 있지만, 기본 100ms라면 저널을 채우는 촘촘한 루프가 됩니다. 더 긴 RestartSec를 우선하고, systemd 254 이상이라면 RestartSteps=와 RestartMaxDelaySec=도 고려해 보세요.
제가 직접 친 systemctl restart도 제한에 포함되나요?
네. 제한은 무엇이 계기였든 유닛의 시작 횟수를 셉니다. 서비스를 반복해서 재시작하는 배포 스크립트나, 짧은 간격으로 연달아 친 재시작 몇 번이 프로그램이 한 번도 죽지 않았는데도 제한을 걸 수 있습니다.
StartLimitBurst를 설정했는데 systemctl show는 여전히 5를 보여줍니다.
설정이 어디로 갔는지 확인하세요. StartLimitIntervalSec=와 StartLimitBurst=는 [Unit] 섹션에 문서화돼 있으니 drop-in에서도 [Unit] 아래에 두세요. 그런 다음 systemctl cat myapp과 systemctl show myapp -p StartLimitBurst로 합쳐진 결과를 확인합니다. systemctl edit 대신 파일을 직접 고쳤다면 systemctl daemon-reload로 다시 읽게 하세요.
제한에 걸렸을 때 systemd가 더 큰 조치를 하게 할 수 있나요?
네. [Unit] 섹션의 StartLimitAction=은 reboot, poweroff 같은 다른 실패 조치와 같은 값을 받고 기본값은 none입니다. 그 유닛 없이는 머신이 쓸모없는 경우를 위한 것이라, 일반 서비스라면 systemctl --failed에 알림을 거는 편이 대개 낫습니다.
참고 자료
- systemd — systemd.unit(5) (StartLimitIntervalSec= and StartLimitBurst= in [Unit]: units started more than burst times within interval are not permitted to start any more; 0 disables the limit; StartLimitAction=; systemctl reset-failed flushes the start rate counter)
- systemd — systemd.service(5) (Restart= is subject to unit start rate limiting configured with StartLimitIntervalSec= and StartLimitBurst=; RestartSec= defaults to 100ms; RestartSteps= and RestartMaxDelaySec= added in version 254)
Haneul Seo
Infrastructure engineer · 10+ years running Linux fleets
같은 카테고리 다른 글
SSH: Received disconnect ... Too many authentication failures
agent가 서버가 허용하는 것보다 많은 키를 내밀고 있습니다. sshd가 들여다보는 공개키 하나하나가 MaxAuthTries 시도를 한 번씩 소모하고 — 기본 6회, 하드닝된 호스트에서는 3회인 경우가 많습니다 — 정작 맞는 키는 차례가 오기 전에 연결이 끊깁니다. IdentitiesOnly=yes와 명시적 IdentityFile로 키 하나를 고정하면 시도 횟수가 1로 떨어집니다.
Active Directory: 복제가 error 1722(The RPC server is unavailable)로 실패
RPC는 하위 계층이 연결에 실패했을 때 1722(0x6ba, RPC_S_SERVER_UNAVAILABLE)를 보고합니다. 따라서 진짜 원인은 RPC 자신이 아니라 DNS, 차단된 포트, 두 도메인 컨트롤러 중 한쪽의 호스트 설정입니다. repadmin이 어느 파트너가 실패하는지 알려주고, dcdiag /test:dns가 이름 해석을 배제하며, Test-NetConnection과 동적 포트 범위가 방화벽 문제를 가릅니다. 가장 흔한 실수는 TCP 135만 허용하고 49152–65535는 막아 둔 규칙입니다.
Windows Server RDS: The remote session was disconnected because there are no Remote Desktop License Servers available to provide a license
120일 RD Licensing 유예 기간이 끝났는데 세션 호스트가 쓸 수 있는 라이선스 서버가 없어 세션을 거부하는 상황입니다. GetGracePeriodDays 의 DaysLeft 가 0 이고 SpecifiedLSList 가 비어 있으면 몇 초 만에 확인됩니다. 해결은 활성화된 라이선스 서버와, 호스트 버전을 감당할 만큼 새 CAL 입니다. 2019 CAL 은 2022 세션 호스트를 서비스하지 못합니다. 여기에 배포 또는 Licensing 정책 설정과 두 서버 사이의 RPC 포트 개방이 따라옵니다.
Windows 11: NAS 공유 폴더를 열 때 "조직의 보안 정책이 인증되지 않은 게스트 액세스를 차단" (0x80070035)
Windows 10 Enterprise/Education/Pro for Workstations, Windows 11 Pro, Windows Server 2019 이후의 SMB 클라이언트는 기본적으로 게스트 로그온을 거부하고, Windows 11 24H2 Enterprise/Pro/Education 은 게스트 세션이 할 수 없는 SMB 서명까지 요구합니다. 그래서 게스트 접근만 제공하는 NAS 공유는 'block unauthenticated guest access' 대화상자, Error code 0x80070035, 또는 System error 3227320323 으로 실패하고, SmbClient/Security 로그에 Event ID 31017 'Rejected an insecure guest logon' 이 남습니다. Microsoft 가 권하는 해결은 NAS 에 실제 계정을 만들고 펌웨어에서 서명을 지원하게 하는 것입니다. Set-SmbClientConfiguration -EnableInsecureGuestLogons $true(24H2 는 -RequireSecuritySignature $false 까지)는 비상구이며, 그 클라이언트의 서명과 암호화를 포기하는 대가가 있습니다.
Ubuntu/Debian: E: Could not get lock /var/lib/dpkg/lock-frontend — 누가 쥐고 있고 어떻게 기다리는지
다른 패키지 관리자, 대개 부팅 때 persistent systemd 타이머로 발화한 Ubuntu 의 unattended-upgrades 가 여러분의 apt-get 이 도는 동안 dpkg 프런트엔드 락을 쥐고 있는 것입니다. Ubuntu 는 apt 바이너리에만 binary::apt::DPkg::Lock::Timeout "120" 을 실어 두어서 apt-get 은 즉시 포기하고 apt 는 기다립니다. 메시지의 PID 를 읽고, 실행이 끝나게 두거나 apt-get 에 -o DPkg::Lock::Timeout=<초> 를 주고, dpkg --configure -a 는 실제로 중단된 실행 뒤에만 쓰고, 락 파일은 절대 지우지 마세요. 보유자가 종료하면 커널이 푸는 fcntl 락입니다.
macOS: xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools)
macOS의 /usr/bin에 있는 git·make·clang 같은 개발 명령은 활성 developer 디렉터리로 넘겨주는 shim이고, xcrun은 xcode-select가 가리키는 디렉터리에 도구가 없다고 보고하는 것입니다 — 대개 macOS 메이저 업그레이드가 /Library/Developer/CommandLineTools를 비워 두었거나, Xcode가 옮겨지거나 삭제된 경우입니다. xcode-select -p와 패키지 영수증을 확인한 뒤 xcode-select --install로 Command Line Tools를 다시 설치하거나, 실제로 있는 Xcode를 xcode-select로 가리키면 됩니다.