Критичні служби мають працювати безвідмовно: веб-сервер, база даних чи VPN не повинні просто падати й чекати, поки ви помітите. Добра новина: стандартні інструменти Linux у поєднанні з systemd дозволяють грамотно налаштувати автоматичне відновлення. Я покажу, як зробити це через Restart=, watchdog, systemd timers і прості bash скрипти — без магії, тільки зрозумілі кроки.

Чому саме systemd: watchdog, Restart і timers

Systemd — це не лише менеджер служб, а й гнучкий інструмент для автоматизації задач і Linux моніторинг. Нам потрібні три механізми:

  • Restart= — базове автоперезапускання, якщо процес аварійно завершується.
  • Watchdog — якщо сервіс періодично надсилає «пульс» (WATCHDOG=1), systemd вважає його здоровим; інакше перезапускає.
  • Timers — планувальник перевірок (аналог cron), який запускає bash скрипти для health-check’ів і реакцій.

Працюємо в термінал Linux, використовуючи зрозумілі linux команди, і будуємо рішення шар за шаром.

Покроково: налаштовуємо автоматичне відновлення

Крок 1. Базова самовідновлюваність через Restart=

Почніть з безпечного автоперезапуску, якщо процес падає. Замість редагування системного юніта напряму створіть drop-in конфіг:

sudo systemctl edit nginx.service

Додайте:

[Service]
Restart=on-failure
RestartSec=5s
StartLimitIntervalSec=2min
StartLimitBurst=5

Потім перезавантажте конфіг і застосуйте:

sudo systemctl daemon-reload
sudo systemctl restart nginx.service

Тепер, якщо nginx упаде з помилкою, systemd спробує його підняти з розумним бекоффом. Це проста, але вже дієва страховка.

Крок 2. Watchdog для служб, які вміють повідомляти

Якщо сервіс може надсилати сповіщення systemd (sd_notify), додайте watchdog. Ідея: поки процес регулярно каже «я живий», systemd не чіпає його; якщо замовк — перезапуск.

Приклад юніта з підтримкою watchdog:

[Unit]
Description=MyApp (notify + watchdog)
After=network.target

[Service]
Type=notify
WatchdogSec=30s
ExecStart=/usr/local/bin/myapp --notify
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

Додаток має раз на <30s надсилати sd_notify("WATCHDOG=1"). Якщо це ваша програма на C/Go/Rust — додайте підтримку sd_notify. Для скриптів можна скористатися systemd-notify.

Крок 3. Watchdog-wrapper для «німого» сервісу

Що робити, якщо ваш демон не вміє sd_notify? Обгорнемо його у скрипт, який:

  1. Стартує процес.
  2. Регулярно перевіряє health (порт/HTTP).
  3. Надсилає WATCHDOG=1, якщо все добре, або завершується з помилкою, щоб systemd перезапустив.

Скрипт:

sudo tee /usr/local/bin/myapp-watchdog.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
APP_CMD="${1:-/usr/local/bin/myapp}"
HEALTH_URL="${2:-http://127.0.0.1:8080/health}"

systemd-notify --ready || true
"$APP_CMD" &
APP_PID=$!
trap 'kill $APP_PID 2>/dev/null || true' EXIT

while kill -0 "$APP_PID" 2>/dev/null; do
  if curl -fsS --max-time 2 "$HEALTH_URL" >/dev/null; then
    systemd-notify WATCHDOG=1 || true
  else
    exit 1
  fi
  sleep 5
done
exit 1
EOF
sudo chmod +x /usr/local/bin/myapp-watchdog.sh

Юніт із watchdog:

sudo tee /etc/systemd/system/myapp.service >/dev/null <<'EOF'
[Unit]
Description=MyApp wrapped with watchdog
After=network.target

[Service]
Type=notify
WatchdogSec=30s
ExecStart=/usr/local/bin/myapp-watchdog.sh "/usr/local/bin/myapp" "http://127.0.0.1:8080/health"
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

Тут ми отримали watchdog без змін у самому сервісі, використавши bash скрипти.

Крок 4. Перевірка здоров'я через systemd timer + bash

Як додатковий рівень захисту зробимо періодичний health-check, який перезапускає сервіс, якщо HTTP/порт недоступний. Це ідеальна зона для cron та systemd timers (але ми віддамо перевагу timers для інтеграції з журналами).

sudo tee /usr/local/bin/healthcheck.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
SERVICE="${1:-nginx}"
URL="${2:-http://127.0.0.1/}"
LOGTAG="healthcheck-$SERVICE"

if curl -fsS --max-time 5 "$URL" >/dev/null; then
  logger -t "$LOGTAG" "OK $URL"
else
  logger -t "$LOGTAG" "FAIL $URL — restarting $SERVICE"
  systemctl restart "$SERVICE"
fi
EOF
sudo chmod +x /usr/local/bin/healthcheck.sh

Сервіс і таймер:

sudo tee /etc/systemd/system/healthcheck@.service >/dev/null <<'EOF'
[Unit]
Description=Healthcheck for %i
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/healthcheck.sh %i http://127.0.0.1/
EOF

sudo tee /etc/systemd/system/healthcheck@nginx.timer >/dev/null <<'EOF'
[Unit]
Description=Healthcheck timer for nginx

[Timer]
OnBootSec=2m
OnUnitActiveSec=1m
AccuracySec=15s
Unit=healthcheck@nginx.service

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now healthcheck@nginx.timer
systemctl list-timers --all | grep healthcheck

Тепер щохвилини скрипт перевірятиме доступність сторінки і, за потреби, перезапустить сервіс. Логи дивіться в journalctl -u healthcheck@nginx.service та через logger у системному журналі — зручно для Linux моніторинг.

Альтернативні способи

  • cron + bash: якщо вам принципово cron, додайте запис у /etc/cron.d/ з викликом healthcheck.sh. Але systemd timers дають кращу інтеграцію з юнітами й журналами.
  • Monit: окремий наглядач із правилами health-check, перезапусками і алертами. Добре, але ще один демоник у системі.
  • Hardware watchdog: апаратний «собачок» через /dev/watchdog для всього ядра/системи. Для критично важливих серверів — топ, але складніше у конфігурації.

GUI-спосіб (Cockpit)

Якщо вам зручніше через веб-інтерфейс, встановіть Cockpit — він уміє керувати службами і таймерами. Це зручно для «живої» діагностики без складних команд.

# Debian/Ubuntu
sudo apt update && sudo apt install -y cockpit
sudo systemctl enable --now cockpit.socket
# Відкрийте https://<IP-сервера>:9090 у браузері, перейдіть до System -> Services/Timers

У Cockpit легко вмикати/вимикати таймери, дивитися статуси та журнали, не виходячи з браузера 🙂

FAQ

Сервіс не перезапускається, хоч Restart= задано.
Перевірте, чи не «вибили» ліміти: systemctl status <svc> покаже StartLimitHit. Підвищте StartLimitBurst або інтервал, і перегляньте причину падіння в journalctl -u <svc>.

Watchdog не працює.
Для watchdog потрібен Type=notify і регулярне sd_notify("WATCHDOG=1"). Якщо ваш процес цього не робить — використайте wrapper, як у прикладі.

Як протестувати перезапуск без реального крашу?
Тимчасово зробіть health-check, який гарантовано фейлиться (URL 127.0.0.1:1), або зупиніть сокет/порт. Для watchdog-wrapper просто поверніть ненульовий код у скрипті.

Timer не запускається.
Перевірте systemctl status healthcheck@nginx.timer. Не забудьте daemon-reload після створення юнітів і enable --now для таймера. Також звірте час системи.

Де дивитись логи?
journalctl -u myapp.service -e, journalctl -u healthcheck@nginx.service -e, а також системний журнал для записів logger. Логи — ваш найкращий друг у діагностиці.

Порада від Kernelka

Не робіть інтервали занадто агресивними. Надто частий перезапуск може погіршити ситуацію і створити петлю. Краще стабільний Linux моніторинг плюс помірні таймаути та автоматизація задач у кілька рівнів: Restart=, watchdog і незалежний health-check таймер. І завжди тестуйте зміни на staging перед продом ⚙️

Підсумок

  • Почніть із Restart=on-failure і безпечних лімітів запусків.
  • Додайте watchdog там, де сервіс може або де його обгортає скрипт.
  • Створіть зовнішній перевіряючий таймер для HTTP/портів.
  • Використовуйте bash скрипти для простих health-check’ів та реакцій.
  • Для наочності можна керувати всім через Cockpit.
  • Регулярно перевіряйте журнали й метрики — це основа стабільності.