systemd로 서버 크래시 자동 복구 — Restart=always 한 줄이면 끝 (2026)

새벽 3시에 서버 프로세스가 죽었습니다. 사용자들은 접속이 안 된다고 불평하고, 우리는 아침에야 알아차렸죠. 새벽 알림을 안 켜둬서 5시간 동안 서비스가 멈춰 있었습니다. 이런 사고를 막으려면 프로세스가 죽어도 자동으로 다시 띄워주는 장치가 필요합니다. systemd가 바로 그 역할을 합니다. Restart=always 한 줄이면 끝입니다.

왜 systemd인가

현대 리눅스(Ubuntu, Debian, CentOS, RHEL, Arch 등)는 부팅부터 서비스 관리까지 전부 systemd가 담당합니다. systemd는 부팅 시 자동으로 서비스를 띄우고, 서비스가 죽으면 정책에 따라 재시작합니다. 직접 작성한 Node.js 서버, Python 스크립트, Go 바이너리도 systemd 서비스로 등록하면 OS 차원에서 관리해 줍니다.

예전에는 nohup, screen, supervisord, pm2 같은 도구로 프로세스를 띄웠습니다. systemd는 이런 도구들이 하는 일을 OS 수준에서 통합해서 처리합니다. 별도 데몬을 설치할 필요가 없고, 부팅 시 자동 시작, 크래시 시 자동 복구, 로그 수집까지 한 번에 해결됩니다.

핵심 한 줄: Restart=always

systemd 설정 파일의 핵심은 Restart=always 한 줄입니다. 이게 있으면 프로세스가 어떤 이유로 종료되든 다시 실행합니다. 정상 종료(exit 0)든, 크래시(segfault)든, OOM으로 죽든 전부 재시작합니다. 서버가 안 죽는다고 확신할 수 있는 유일한 방법입니다.

# /etc/systemd/system/myapp.service
[Unit]
Description=My App
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/app
ExecStart=/usr/bin/node /opt/app/server.js
Restart=always
RestartSec=3
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

systemd는 죽지 마라고 명령한 일에 대해서는 정말 끈질기게 살리려고 합니다. 좀비 영화의 생존자 같습니다. 죽으면 RestartSec에 정한 시간(기본 100ms, 추천 3초)을 기다렸다가 다시 띄웁니다. RestartSec=3은 3초 대기 후 재시작이라는 뜻입니다. 너무 짧으면 CPU가 과부하에 걸리고, 너무 길면 다운타임이 길어지니 3~5초가 적당합니다.

활성화: enable –now 한 번이면 끝

설정 파일을 만들었으면 systemd에게 알려주고 실행합니다. 한 번이면 끝납니다.

# 설정 파일 다시 읽기
sudo systemctl daemon-reload

# 활성화 + 즉시 시작
sudo systemctl enable --now myapp

# 상태 확인
sudo systemctl status myapp

# 활성화 여부 확인 (부팅 시 자동 시작)
sudo systemctl is-enabled myapp

enable은 부팅 시 자동 시작을 켜는 명령이고, –now를 붙이면 지금 즉시 시작합니다. 두 가지가 한 번에 해결됩니다. 이제 부팅 시 자동 시작, 크래시 시 자동 복구, 두 가지가 한 번에 켜집니다.

실제 크래시 시나리오로 검증하기

진짜로 재시작이 잘 되는지 검증해 봅니다. 프로세스를 강제로 죽여보면 됩니다.

# 프로세스 PID 찾기
sudo systemctl status myapp | grep "Main PID"
# Main PID: 12345

# 강제 종료
sudo kill -9 12345

# 3초 뒤에 상태 확인
sleep 3
sudo systemctl status myapp
# Main PID: 12367 (다른 PID로 다시 살아남)

3초 안에 다른 PID로 살아나는 걸 볼 수 있습니다. 자동 복구가 작동하는 겁니다. 실제 운영에서는 OOM, 세그폴트, 네트워크 끊김 등 다양한 이유로 프로세스가 죽는데, 전부 이 패턴으로 자동 복구됩니다.

로그는 journalctl로

서비스가 왜 죽었는지, 몇 번 재시작했는지는 journalctl로 확인합니다. systemd는 서비스 로그를 자동으로 수집해서 저널에 저장합니다. 별도 로그 파일을 안 만들어도 됩니다.

# 최근 로그 50줄
sudo journalctl -u myapp -n 50

# 실시간 로그 follow
sudo journalctl -u myapp -f

# 오늘 로그만
sudo journalctl -u myapp --since today

# 특정 시간 이후
sudo journalctl -u myapp --since "2026-07-13 03:00:00"

# 재시작 기록만 필터
sudo journalctl -u myapp | grep -i "started|stopped|failed"

크래시 원인을 찾을 때 –since today로 오늘 로그만 보거나, 특정 시간대로 좁혀서 보는 게 유용합니다. systemd는 로그가 자동 로테이션되어 디스크가 꽉 차지 않습니다. 오래된 로그는 자동으로 삭제됩니다.

Restart 정책 비교

정책 재시작 조건 추천 용도
Restart=no 재시작 안 함 일회성 스크립트
Restart=on-success exit 0이 아닐 때만 정상 종료는 존중
Restart=on-failure 비정상 종료 시 exit 0은 그만둠 허용
Restart=always 무조건 재시작 웹 서비스 (추천)
Restart=on-abort 시그널 비정상 종료 좁은 조건

웹 서비스라면 무조건 Restart=always로 잡으세요. on-failure로 잡으면 정상 종료(exit 0)한 경우 재시작을 안 하는데, 서비스가 스스로 exit 0으로 끝나는 경우가 꽤 있습니다. graceful shutdown을 잘못 구현했다거나, 의존성 라이브러리가 exit 0을 호출한다거나. always가 가장 안전합니다.

주의점: 무한 재시작 루프

Restart=always의 함정이 있습니다. 설정 파일이 잘못돼서 프로세스가 1초 만에 죽는 경우입니다. 그럼 systemd는 1초 주기로 계속 재시작을 시도하고 CPU가 100%로 치솟습니다. 이걸 막으려면 StartLimitBurst와 StartLimitIntervalSec으로 재시도 횟수 제한을 겁니다.

# /etc/systemd/system/myapp.service [Unit] 섹션에
[Unit]
StartLimitIntervalSec=60
StartLimitBurst=5
# 60초 안에 5회 이상 재시작 실패 시 더 이상 시도 안 함

이렇게 해두면 설정 오류로 인한 무한 루프를 막을 수 있습니다. 5회 실패 후에는 systemctl reset-failed myapp로 수동 초기화해야 다시 시도합니다.

다른 유용한 설정

Restart 외에도 자주 쓰는 설정들이 있습니다. Environment로 환경변수를 주입하고, User로 실행 계정을 지정하고, WorkingDirectory로 작업 디렉토리를 잡습니다. 메모리 보호를 위해 MemoryMax를 설정하면 OOM 상황에서 서버 전체가 죽는 걸 막을 수 있습니다.

[Service]
# 환경변수
Environment=DATABASE_URL=postgresql://localhost/myapp
Environment=PORT=3000

# 자원 제한 (OOM 보호)
MemoryMax=512M
CPUQuota=200%

# 파일 디스크립터 한도
LimitNOFILE=65536

# 보안 강화
NoNewPrivileges=true
ProtectSystem=strict

systemd 타이머로 cron 대체하기

systemd로 서비스를 관리하기 시작했다면, 예약 작업도 systemd timer로 통합할 수 있습니다. cron 대신 systemd timer를 쓰면 로그가 journalctl에 자동으로 수집되고, 실패 시 재시도, 의존성 순서 지정까지 가능합니다. 백업 스크립트, 로그 로테이션, 정기 헬스체크를 timer로 돌리면 관리가 한곳에 모입니다.

# /etc/systemd/system/backup.service
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh

# /etc/systemd/system/backup.timer
[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

# 활성화
sudo systemctl enable --now backup.timer

Docker Compose로 띄운 서비스도 systemd로 관리하면 더 안정적입니다. Docker Compose 셀프 호스팅 가이드systemd 공식 문서를 참고하세요.

서버를 수면 없이 운영하려면 systemd Restart=always 한 줄로 시작하세요. 부팅 시 자동 시작, 크래시 시 자동 복구, 로그 수집까지 한 번에 해결됩니다. DevOps 실전 팁을 매주 받아보시려면 구독해 주세요.

관련 글