홈/인프라/systemd 자동 재시작 가이드: Restart=always vs on-failure와 start-limit-hit 해결
인프라systemd리눅스 서버 운영

systemd 자동 재시작 가이드: Restart=always vs on-failure와 start-limit-hit 해결

systemd 서비스 자동 재시작을 Restart=always와 on-failure 차이부터 정리. StartLimitIntervalSec 무한 재시작 함정과 start-limit-hit 에러, systemctl reset-failed 복구법, journalctl 진단, .service 전체 샘플까지 바로 적용하세요.

systemd 자동 재시작 가이드: Restart=always vs on-failure와 start-limit-hit 해결
목차
  1. 프로세스가 죽었는데 아무도 안 살려준다
  2. Restart 옵션 7종 + RestartSec 비교
  3. 무한 재시작의 함정: start-limit-hit는 왜 뜨는가
  4. 죽은 원인 진단과 실전 unit 파일
  5. 결론: 자동 재시작 체크리스트
  6. 자주 묻는 질문 (FAQ)

systemd 자동 재시작 완벽 가이드: Restart=always vs on-failure와 start-limit-hit 해결

프로세스가 죽었는데 아무도 안 살려준다

새벽 3시, 서비스가 조용히 죽었습니다. 모니터링 알람이 울리고 나서야 SSH로 들어가 systemctl start를 친 경험, 운영자라면 한 번쯤 있을 겁니다. 쿠버네티스 같은 오케스트레이터가 없는 단일 VM·베어메탈 환경에서는 이 "죽으면 알아서 살리기"를 누군가가 책임져야 하는데, 요즘은 supervisord나 pm2를 따로 올리지 않고 systemd로 일원화하는 흐름이 사실상 표준입니다. 배포 도구가 이미 깔려 있고, 부팅 자동 시작·로그 수집(journald)·의존성 관리까지 한 번에 되니까요.

그런데 막상 Restart=always만 붙였다가 "start request repeated too quickly" 에러로 서비스가 영영 안 올라오는 함정에 빠지는 분이 많습니다. 이 글은 Restart 옵션의 정확한 차이부터, 그 무한 재시작 함정을 진단하고 푸는 명령어, 그리고 복붙 가능한 .service 전체 샘플까지 한 번에 정리합니다.

Restart 옵션 7종 + RestartSec 비교

가장 헷갈리는 게 always와 on-failure의 차이입니다. 핵심은 "프로세스가 스스로 깨끗하게 끝났을 때(exit 0)도 다시 띄울 것이냐" 입니다. Restart=에 쓸 수 있는 값은 아래 7가지입니다. systemd.service(5)

Restart 값깨끗한 종료(exit 0 또는 SIGHUP·SIGINT·SIGTERM·SIGPIPE)exit 0이 아닌 종료비정상 시그널 종료(SIGSEGV·SIGABRT·SIGKILL 등)시작·중지 timeoutwatchdog timeout
no (기본값)❌❌❌❌❌
on-success✅❌❌❌❌
on-failure❌✅✅✅✅
on-abnormal❌❌✅✅✅
on-watchdog❌❌❌❌✅
on-abort❌❌✅❌❌
always✅✅✅✅✅

SIGTERM처럼 "깔끔하게 종료해달라"는 시그널은 기본적으로 "클린 종료"로 간주되어 on-failure에서는 재시작하지 않습니다. SIGKILL, SIGSEGV, SIGABRT 등은 비정상으로 보고 재시작합니다. 특정 종료 코드를 정상으로 취급하려면 SuccessExitStatus=, 재시작에서 빼려면 RestartPreventExitStatus=를 씁니다.

systemctl stop은 재시작을 일으키지 않습니다. 어떤 Restart 값이든 마찬가지입니다. 관리자가 systemctl stop(또는 systemctl kill)으로 내린 것은 systemd가 직접 요청한 중지라서 재시작 대상이 아닙니다. 그래서 always를 걸어도 stop하면 얌전히 멈춥니다.

언제 무엇을 쓰나?

  • 웹 서버·API·데몬 등 항상 떠 있어야 하는 상주 서비스 → on-failure나 always 모두 쓰입니다. 둘의 차이는 프로세스가 스스로 깨끗하게 종료했을 때만 생깁니다. 상주 서비스가 스스로 exit 0으로 끝나는 것도 장애로 본다면 always, 정상 종료로 본다면 on-failure를 고르세요.
  • 무조건 떠 있어야 하고, 정상 종료가 곧 비정상인 서비스 → always.
  • 배치성 잡 → on-failure 또는 no. 정상 완료(exit 0) 후에 다시 실행되면 안 되기 때문입니다.
  • watchdog(WatchdogSec=)으로 헬스체크하는 서비스에서 응답 없음만 복구 → on-watchdog.

RestartSec: 재시작 전에 숨 한 번 쉬기

INI
RestartSec=5s

재시작 전 대기 시간입니다. 기본값은 100ms인데, 이걸 그냥 두면 DB가 죽었거나 포트가 막힌 상황에서 서비스가 초당 수십 번 부팅을 시도하며 CPU 폭주·DB 연결 폭주를 일으킵니다. 실무에서는 RestartSec=5(초) 정도를 권장합니다. 외부 의존성(DB, 메시지큐) 복구를 기다릴 여유를 주는 값입니다.

무한 재시작의 함정: start-limit-hit는 왜 뜨는가

여기가 이 글의 핵심입니다. systemd는 "재시작이 너무 빨리 반복되면 포기"하는 안전장치를 갖고 있습니다.

INI
[Unit]
# systemd 기본값
StartLimitIntervalSec=10
StartLimitBurst=5

의미는 "10초 안에 5번을 초과해 재시작하면 더 이상 살리지 않는다" 입니다. 이 조건에 걸리면 로그에 이렇게 찍힙니다.

CODE
myapp.service: Start request repeated too quickly.
myapp.service: Failed with result 'start-limit-hit'.
Failed to start myapp.service.

ExecStart가 즉시 실패하는 버그(설정 오타, 포트 충돌 등)가 있으면, RestartSec가 짧을 경우 10초 안에 순식간에 5번을 넘겨 이 상태에 빠집니다. 이때부터는 systemctl start를 쳐도 올라오지 않습니다. 카운터가 잠겨 있기 때문입니다.

한계에 걸렸을 때 systemctl status는 이렇게 보입니다.

TEXT
● myapp.service - My App Daemon
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
     Active: failed (Result: start-limit-hit) since Fri 2026-07-10 09:14:22 KST

Jul 10 09:14:22 host systemd[1]: myapp.service: Scheduled restart job, restart counter is at 3.
Jul 10 09:14:22 host systemd[1]: myapp.service: Start request repeated too quickly.
Jul 10 09:14:22 host systemd[1]: myapp.service: Failed with result 'start-limit-hit'.

Result: start-limit-hit가 보이면 "죽어서 안 살아나는" 게 아니라 "너무 자주 죽어서 systemd가 재시작을 포기한" 상태입니다.

해결책 3가지

① 한도 자체를 조정한다

INI
[Unit]
StartLimitIntervalSec=60
StartLimitBurst=3

② RestartSec를 늘려 burst를 회피한다 — 가장 중요한 핵심 공식이 있습니다.

CODE
RestartSec × StartLimitBurst > StartLimitIntervalSec

이 부등식을 만족하면, 정해진 인터벌 안에서 burst 횟수를 채우는 게 물리적으로 불가능해져 start-limit-hit에 영원히 걸리지 않습니다. 예를 들어 RestartSec=5, StartLimitBurst=3이면 5×3=15이므로 StartLimitIntervalSec를 15보다 작게(예: 10) 잡으면 한도에 걸리지 않습니다. 반대로 진짜로 망가진 서비스를 빠르게 멈추고 싶으면 이 공식을 일부러 깨서 일정 횟수 후 멈추게 둘 수도 있습니다.

③ 제한을 아예 없앤다 — 어떤 일이 있어도 계속 재시작하길 원한다면:

INI
[Unit]
StartLimitIntervalSec=0

이 값이 0이면 카운팅이 비활성화되어 무한 재시도합니다. (단, 진짜 버그가 있으면 CPU를 갉아먹으니 RestartSec는 반드시 넉넉히 주세요.)

systemctl reset-failed: 잠긴 카운터 풀기

start-limit-hit에 걸린 상태에서는 설정만 고치고 start해도 안 됩니다. 먼저 카운터를 초기화해야 합니다.

Bash
# 1) unit 파일 수정 후
sudo systemctl daemon-reload

# 2) 실패 카운터 리셋 (이게 핵심!)
sudo systemctl reset-failed myapp.service

# 3) 다시 기동
sudo systemctl start myapp.service

# 4) failed 상태가 풀렸는지 확인
systemctl status myapp.service
systemctl is-failed myapp.service   # 'active' 또는 'inactive'면 정상

reset-failed 없이 헤매다 "왜 start가 먹통이지?" 하는 경우가 정말 많습니다. start-limit 로그를 봤다면 이 명령어를 먼저 떠올리세요.

죽은 원인 진단과 실전 unit 파일

자동 재시작을 걸기 전에, 왜 죽었는지부터 봐야 합니다. journald가 다 기록해 둡니다.

Bash
# 최근 50줄 (페이저 없이 바로 출력)
journalctl -u myapp.service -n 50 --no-pager

# 최근 10분간 로그만
journalctl -u myapp.service --since "10 min ago"

# 현재 상태와 마지막 종료 코드/시그널
systemctl status myapp.service

# 실제 적용된 재시작 관련 값 확인 (오타·daemon-reload 누락 점검)
systemctl show myapp.service -p Restart -p RestartSec -p StartLimitBurst -p StartLimitIntervalUSec

systemctl status의 마지막 줄에 Main PID exited, code=exited, status=1 또는 code=killed, signal=SEGV 같은 단서가 나옵니다. 여기서 종료 원인을 잡고 나서 Restart 정책을 정하는 게 순서입니다.

실무 경험 한마디: 저는 RestartSec를 짧게 두고 외부 DB 장애를 만났다가, 재시작 폭주로 DB 커넥션 풀이 더 빨리 고갈되는 악순환을 본 적이 있습니다. 그 뒤로는 상주 서비스의 기본값을 on-failure + RestartSec=5 + 넉넉한 StartLimit로 고정해 두고 시작합니다. "빨리 살리기"보다 "안전하게 살리기"가 결국 가용성에 더 좋았습니다.

따라치면 되는 .service 전체 샘플

/etc/systemd/system/myapp.service:

INI
[Unit]
Description=My Application Service
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=3

[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yaml
Restart=on-failure
RestartSec=5
# 표준출력/에러를 journald로
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

적용 절차:

Bash
# 파일 저장 후 systemd에 변경 알림
sudo systemctl daemon-reload

# 부팅 자동 시작 등록 + 즉시 기동
sudo systemctl enable --now myapp.service

# 잘 떴는지 확인
systemctl status myapp.service

참고: StartLimitIntervalSec/StartLimitBurst는 [Unit] 섹션 지시어입니다(systemd 230부터 이 이름). [Service]에 StartLimitIntervalSec를 적으면 "Unknown key"로 무시되고, [Service]에서는 호환용으로 구 이름 StartLimitInterval과 StartLimitBurst만 받아들입니다. 적용값은 systemctl show myapp.service -p StartLimitIntervalUSec -p StartLimitBurst로, 문법 경고는 systemd-analyze verify /etc/systemd/system/myapp.service로 확인하세요. systemd.unit(5)

적용 후 예상 결과와 분기:

  • daemon-reload — 출력이 없으면 성공입니다.
  • status가 Active: active (running)이면 정상, Loaded: ... enabled면 부팅 자동 시작도 켜진 것입니다.
  • inactive (dead) → start를 안 했거나 enable만 한 경우입니다. sudo systemctl start myapp.service.
  • failed (Result: exit-code) → 앱 자체가 비정상 종료했습니다. journalctl -u myapp.service -n 50으로 앱 에러를 확인합니다.
  • failed (Result: start-limit-hit) → 위 StartLimit 절대로 원인을 고친 뒤 reset-failed.
  • journalctl -u myapp.service -f에 Scheduled restart job이 반복해서 찍히면 크래시 루프입니다.

결론: 자동 재시작 체크리스트

상주 서비스라면 아래 기본값으로 시작하는 것을 권장합니다.

  • ✅ Restart=on-failure — 프로세스가 스스로 정상 종료(exit 0)하면 다시 띄우지 않음 (systemctl stop은 어떤 값이든 재시작하지 않음)
  • ✅ RestartSec=5 — CPU·DB 폭주 방지
  • ✅ [Unit]에 StartLimitIntervalSec=60, StartLimitBurst=3 — 진짜 망가진 서비스는 멈추게
  • ✅ 무한 재시도가 꼭 필요하면 StartLimitIntervalSec=0
  • ✅ start-limit-hit를 만나면 → daemon-reload → reset-failed → start 순서
  • ✅ 정책 정하기 전에 journalctl -u로 종료 원인부터 확인

RestartSec × StartLimitBurst > StartLimitIntervalSec 공식을 기억해 두면 무한 재시작 함정을 피하는 데 도움이 됩니다.

자주 묻는 질문 (FAQ)

Q. "start request repeated too quickly"가 뜨고 systemctl start를 해도 안 올라옵니다. A. start-limit 카운터가 잠긴 상태입니다. sudo systemctl reset-failed <서비스명>으로 카운터를 초기화한 뒤 sudo systemctl start <서비스명>을 다시 실행하세요. 근본 원인(ExecStart 즉시 실패)을 고치지 않으면 또 걸리니, journalctl -u로 종료 원인을 함께 확인하세요.

Q. Restart=always와 on-failure 중 무엇을 써야 하나요? A. systemctl stop은 두 값 모두 재시작을 일으키지 않으므로, 차이는 프로세스가 스스로 깨끗하게 종료(exit 0)했을 때뿐입니다. 그때 다시 띄워야 하면 always, 정상 종료로 두어야 하면(배치 잡 등) on-failure를 쓰세요.

Q. 설정을 바꿨는데 적용이 안 됩니다. A. unit 파일을 수정했으면 반드시 sudo systemctl daemon-reload를 먼저 해야 합니다. 그 후 systemctl show <서비스> -p Restart -p RestartSec -p StartLimitBurst로 실제 반영된 값을 확인하세요.

Q. enable과 start의 차이가 뭔가요? A. enable은 부팅 시 자동 시작 등록이고, start는 지금 당장 실행입니다. 둘은 독립적이라 enable만 하면 재부팅 전까지는 안 뜨고, start만 하면 재부팅 후에는 안 뜹니다. 둘 다 원하면 sudo systemctl enable --now <서비스명>으로 한 번에 처리하세요.

확인 정보
✦ ✦ ✦
출처·검증 안내

글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보

이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.

발행 · ·업데이트 ·
주제 관련 공식 문서(자동 연결 · 문장별 근거 아님)GNU/Linux man 페이지 ↗

댓글

첫 번째 댓글을 남겨보세요.