Как создать собственную systemd-службу и запускать её автоматически при загрузке

Опубликовано 14 мин. чтения

Unit-файл строка за строкой: Type, User, WorkingDirectory, Restart и network-online.target. С дословными текстами ошибок и доказательством, что служба действительно переживает перезагрузку.

Программа, которая после перезагрузки не поднимается сама, на сервере не служба, а риск. nohup, screen и tmux удерживают процесс живым ровно до тех пор, пока ничего не происходит. В этой статье собственный unit-файл разбирается строка за строкой, приводятся дословные тексты сообщений об ошибках и описывается, как выбраться, если служба застряла в карусели перезапусков.

Все команды выполняются от root. Проверено на Debian 13 (systemd 257), Debian 12 (252), Ubuntu 24.04 LTS (255) и Ubuntu 22.04 LTS (249). Там, где эти четыре системы расходятся, это указано отдельно.

Что даёт systemd-служба и чего не дают nohup и screen

Разница не в удобстве, а в ответственности. systemd запускает процесс при загрузке в заданном порядке, помещает его в отдельную cgroup, собирает stdout и stderr в журнал, перезапускает после падения и корректно завершает при выключении сигналом SIGTERM. Больнее всего потом не хватает именно cgroup: скрипт, запущенный через nohup, оставляет за собой осиротевшие дочерние процессы, а юнит вычищает свою группу целиком.

Подготовка: программа, пользователь и каталог

Прежде чем появится юнит, должно работать то, что он будет запускать. В качестве примера возьмём скрипт, который пишет в stdout. В этом и смысл: служба под systemd не ведёт лог-файл сама, она пишет в stdout, а systemd складывает вывод в журнал.

mkdir -p /opt/kh-demo
cat > /opt/kh-demo/run.sh <<'EOF'
#!/bin/bash
set -euo pipefail
while true; do
  echo "kh-demo работает, $(date --iso-8601=seconds), PWD=$PWD, GREETING=${GREETING:-не задано}"
  sleep 10
done
EOF
chmod +x /opt/kh-demo/run.sh

Дальше отдельный системный пользователь: --system выдаёт UID меньше 1000, /usr/sbin/nologin закрывает интерактивный вход.

useradd --system --no-create-home --home-dir /opt/kh-demo --shell /usr/sbin/nologin khdemo
id khdemo
chown -R root:khdemo /opt/kh-demo
chmod 750 /opt/kh-demo

Ключевая проверка перед написанием unit-файла: запускается ли программа от этого пользователя? Если её пропустить, потом придётся отлаживать systemd, хотя проблема сидит в самой программе.

timeout 3 runuser -u khdemo -- /opt/kh-demo/run.sh; echo "Exitcode $?"

Exitcode 124 здесь и есть желаемый результат: timeout прервал работающую программу. Любое другое значение означает, что скрипт завершился сам, и тогда виноват не systemd.

Unit-файл строка за строкой

Собственные юниты кладут в /etc/systemd/system/, а не в /lib/systemd/system/ или /usr/lib/systemd/system/: эти два каталога принадлежат менеджеру пакетов и будут перезаписаны при ближайшем apt upgrade.

cat > /etc/systemd/system/kh-demo.service <<'EOF'
[Unit]
Description=KernelHost Demo Worker
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=khdemo
Group=khdemo
WorkingDirectory=/opt/kh-demo
EnvironmentFile=-/etc/kh-demo.env
ExecStart=/opt/kh-demo/run.sh
Restart=on-failure
RestartSec=5s
TimeoutStopSec=20s
SyslogIdentifier=kh-demo
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full

[Install]
WantedBy=multi-user.target
EOF

Секция [Unit]

Description= — это текст, который показывает systemctl status. Технического эффекта нет, но именно от него зависит, поймёте ли вы в три часа ночи, что именно упало. Одно предложение, а не одно слово.

After= задаёт только порядок, но не зависимость. After=network-online.target означает: если этот target и так стартует в текущей загрузке, дождись его. Это не означает, что он будет запущен.

Wants= действительно запрашивает его, причём как слабую зависимость. Если target не поднимется, служба всё равно стартует. Аналог Requires= утянул бы службу за собой в ошибку. Для большинства приложений правильный вариант это Wants=, потому что служба, которая вообще не стартует из-за сетевого тайм-аута, хуже той, которая недолго поработает вхолостую и вернётся благодаря Restart=.

Секция [Service]

User= и Group= задают, от чьего имени работает процесс. Без этих строк служба работает от root. В Debian и Ubuntu useradd по умолчанию создаёт одноимённую группу, поэтому Group=khdemo подходит.

WorkingDirectory= — рабочий каталог процесса. Без этого указания служба стартует в /. Любая программа, которая ищет конфигурации, шаблоны или плагины относительно текущего каталога, тут же ломается. Если указанный каталог не существует, запуск заканчивается кодом 200/CHDIR.

EnvironmentFile= подгружает переменные из файла. Ведущий минус перед путём делает файл необязательным. Без минуса запуск сорвётся, если файла нет. Внутри лежат простые строки KEY=VALUE, а не шелл. export перед ними ошибка, и PORT=$BASE_PORT подставлен не будет.

ExecStart= требует абсолютного пути к исполняемому файлу. Именно на этом правиле спотыкается большинство, поэтому ниже для него отдельный раздел.

TimeoutStopSec= ограничивает, сколько systemd ждёт после SIGTERM, прежде чем послать SIGKILL. По умолчанию это 90 секунд.

SyslogIdentifier= задаёт имя, под которым строки появляются в журнале. Без этой строки отправителем будет run.sh, что бесполезно при фильтрации.

NoNewPrivileges=true запрещает процессу и всем его потомкам получать права через setuid-бинарники. PrivateTmp=true выдаёт службе собственный /tmp. ProtectSystem=full монтирует /usr, /boot и /etc только для чтения. Уровень strict идёт дальше и требует тогда StateDirectory= или ReadWritePaths= для всего, куда нужна запись.

Секция [Install]

WantedBy=multi-user.target отвечает на вопрос, когда служба должна стартовать автоматически. multi-user.target — это обычный многопользовательский режим без графической оболочки, то есть то состояние, до которого доходит сервер. Секцию учитывает только команда systemctl enable, она же создаёт символьную ссылку. Если [Install] нет, enable прерывается с сообщением The unit files have no installation config (WantedBy=, RequiredBy=, Also=, Alias= settings in the [Install] section, and DefaultInstance= for template units). Такую службу можно запустить вручную, но после перезагрузки она уже никогда не вернётся.

Type=simple, Type=exec и Type=forking

Type= отвечает ровно на один вопрос: по какому признаку systemd понимает, что служба запустилась?

При Type=simple служба считается запущенной, как только процесс создан, то есть сразу после fork() и ещё до того, как программа начнёт выполняться. Это значение по умолчанию, и для почти всех современных программ, которые остаются на переднем плане, оно верное.

При Type=exec systemd дополнительно дожидается успешного execve(). Практическая разница существенная: с Type=simple команда systemctl start отрапортует об успехе даже тогда, когда бинарника вообще нет, а ошибка всплывёт только потом в журнале. С Type=exec в том же случае запуск падает сразу. Доступно начиная с systemd 240, то есть на всех четырёх дистрибутивах.

При Type=forking systemd исходит из того, что программа сама уходит в фон: запущенный процесс завершается, дочерний продолжает работу, и именно это завершение считается сигналом об успешном старте. Так ведёт себя классический Unix-демон. Кто выбирает этот тип, почти всегда вынужден добавить ещё PIDFile= с абсолютным путём, иначе systemd будет гадать, какой из оставшихся процессов главный.

Рекомендация однозначная: Type=forking только тогда, когда программу никакими средствами не удаётся удержать на переднем плане. Почти у любого софта для этого есть ключ, чаще всего --foreground, -D FOREGROUND, --no-daemon или daemon off; в конфигурации. Передний план плюс Type=simple дают более короткий юнит, логи попадают в журнал, а Restart= работает предсказуемо.

Типичная неверная комбинация: программа, которая сама уходит в фон, под Type=simple. systemd видит завершение стартового процесса, считает службу законченной и убивает потомков заодно. Симптом — Active: inactive (dead) сразу после systemctl start, который не сообщил ни о какой ошибке. Обратный случай, программа переднего плана под Type=forking, заставляет systemd ждать завершения, которое никогда не наступит: Job for foo.service failed because a timeout was exceeded, через 90 секунд.

Как правильно подключить After=network-online.target

Здесь почти любое руководство обрывается слишком рано. network.target означает лишь то, что подсистема управления сетью запущена, а не то, что IP-адрес уже настроен. Если нужен доступный адрес, например потому что программа привязывается к фиксированному IP, нужен network-online.target.

Но сам по себе этот target не достигается. Он требует, чтобы была включена подходящая служба ожидания, а она отличается в зависимости от того, кто управляет сетью:

systemctl list-unit-files 'systemd-networkd-wait-online.service' 'NetworkManager-wait-online.service' 'ifupdown-wait-online.service' --no-pager

На Ubuntu Server 22.04 и 24.04 сетью управляет netplan вместе с systemd-networkd, там активна systemd-networkd-wait-online.service и network-online.target имеет реальный смысл. В классической установке Debian с ifupdown служба ifupdown-wait-online.service хотя и существует, но не включена. Target тогда считается достигнутым мгновенно, и ожидание, на которое вы рассчитываете, просто не происходит.

На Debian 12 и 13 с ifupdown службу ожидания при необходимости включают один раз:

systemctl enable ifupdown-wait-online.service

Включать две службы ожидания одновременно плохая идея: каждая будет ждать свою подсистему управления сетью, и одна неизбежно упрётся в 90-секундный тайм-аут. Это самая частая причина того, что сервер вдруг грузится на полторы минуты дольше. Надёжнее любого порядка запуска всё равно программа, которая переживает отсутствие соединения при старте, в паре с Restart=on-failure.

Restart, RestartSec и ограничитель частых запусков

Restart=on-failure перезапускает службу при коде выхода, отличном от нуля, при сигнале вроде SIGSEGV и при срабатывании watchdog, но не после выхода с кодом 0 и не после ручного systemctl stop. Restart=always перезапускает вдобавок и после штатного завершения, и это самый быстрый способ построить себе бесконечный цикл.

Против этого у systemd есть встроенный тормоз: по умолчанию разрешено пять попыток запуска за десять секунд (StartLimitBurst=5, StartLimitIntervalSec=10s). После этого systemd сдаётся и сообщает:

kh-demo.service: Start request repeated too quickly.
kh-demo.service: Failed with result 'exit-code'.
Failed to start kh-demo.service - KernelHost Demo Worker.

Служба остаётся в состоянии failed и перестаёт реагировать даже на systemctl start, пока счётчик не сброшен:

systemctl reset-failed kh-demo.service

Две детали здесь регулярно стоят времени. Во-первых, StartLimitBurst= и StartLimitIntervalSec= относятся к секции [Unit], а не к [Service]. В неправильной секции они игнорируются, с предупреждением в журнале, но без ошибки. Во-вторых, ручные вызовы systemctl restart тоже считаются: кто при отладке перезапускает службу пять раз подряд, включает ограничитель собственными руками.

Практическое правило: StartLimitIntervalSec больше, чем RestartSec, умноженное на StartLimitBurst.

На Debian 13 и Ubuntu 24.04 дополнительно есть RestartSteps= и RestartMaxDelaySec= (начиная с systemd 254) для экспоненциально растущих пауз. На Debian 12 и Ubuntu 22.04 этих директив нет, и они просто игнорируются.

Включаем, запускаем и доказываем, что работает

Перед первым запуском стоит проверить синтаксис. Проверка находит опечатки в именах директив, неверно написанные секции и отсутствующие программы, ничего при этом не запуская:

systemd-analyze verify /etc/systemd/system/kh-demo.service

Пустой вывод означает, что всё в порядке. Опечатка вроде WorkingDirectiry= даёт, в зависимости от версии systemd, Unknown key name 'WorkingDirectiry' in section 'Service', ignoring или Unknown key 'WorkingDirectiry' in section [Service], ignoring. Именно здесь рождаются тихие ошибки: systemd игнорирует неизвестные ключи, служба стартует, но ведёт себя не так, как ожидалось.

systemctl daemon-reload

daemon-reload перечитывает unit-файлы, но ничего не перезапускает. Кто забудет про него, получит при следующем systemctl status: Warning: The unit file, source configuration file or drop-ins of kh-demo.service changed on disk. Run 'systemctl daemon-reload' to reload units. Порядок запомнить просто: сначала daemon-reload, потом restart.

systemctl enable kh-demo.service
systemctl start kh-demo.service

Оба шага сразу выполняет systemctl enable --now kh-demo.service.

Теперь та часть, которую большинство руководств опускает: доказательство, что всё действительно заработало, а не просто обошлось без ошибок. Четыре независимых подтверждения.

Первое: символьная ссылка существует. Она и есть весь механизм, стоящий за enable. Если её нет, после перезагрузки служба не поднимется, что бы ни показывал status прямо сейчас.

ls -l /etc/systemd/system/multi-user.target.wants/kh-demo.service
systemctl is-enabled kh-demo.service

Второе: состояние. Решающим является уточнение в скобках в строке Active:. active (running) значит, что процесс работает. active (exited) значит, что программа отработала и никого больше не осталось. Для постоянно работающей службы это ошибка, даже если рядом горит зелёное.

systemctl status kh-demo.service --no-pager
systemctl is-active kh-demo.service

Третье: журнал. Важно не только то, что строки приходят, но и то, что приходят правильные.

journalctl -u kh-demo.service -n 20 --no-pager

Читать в реальном времени: journalctl -u kh-demo.service -f. Только последняя загрузка: -b. Промежуток времени: --since "-1h". Подробнее об этом в статье journalctl: разбор логов в systemd.

Четвёртое: действующая конфигурация. Не файл, а то, что systemd из него сделал. Разница становится важной, как только в игру вступают drop-in-файлы.

systemctl show kh-demo.service -p ExecStart -p User -p WorkingDirectory -p Restart -p MainPID
systemctl cat kh-demo.service

Окончательным доказательством остаётся настоящая перезагрузка. После неё эта команда покажет, не потерялось ли что-нибудь по дороге:

systemctl list-units --type=service --state=failed --no-pager

Самые частые ошибки, дословно

Об ошибках запуска systemd сообщает собственными кодами выхода выше 200. Число в выводе status уже подсказывает, где искать.

КодЗначениеПричина
200/CHDIREXIT_CHDIRWorkingDirectory= не существует или пользователь не может в него зайти
203/EXECEXIT_EXECExecStart= не найден, не является исполняемым или указан неверный интерпретатор
216/GROUPEXIT_GROUPGroup= не существует
217/USEREXIT_USERUser= не существует
219/CGROUPEXIT_CGROUPне удалось создать cgroup
238/STATE_DIRECTORYEXIT_STATE_DIRECTORYStateDirectory= уже существует, но с неправильным владельцем

203/EXEC: неверный путь в ExecStart

В журнале это выглядит как строка вида kh-demo.service: Failed at step EXEC spawning /opt/kh-demo/run.sh: No such file or directory, за которой следует Main process exited, code=exited, status=203/EXEC.

No such file or directory вводит в заблуждение, потому что у этого сообщения три причины. Первая: файла действительно нет, чаще всего из-за опечатки или потому что бинарник лежит в /usr/local/bin, а не в /usr/bin. Вторая: не выставлен бит исполнения, тогда журнал сообщает Permission denied. Третья, самая коварная: файл исполняемый, но его строка shebang указывает в пустоту. Скрипт с #!/usr/bin/python падает на Debian 12 и новее именно так, потому что там существует только /usr/bin/python3. Ядро сообщает об отсутствии интерпретатора, а systemd передаёт это дальше как отсутствие самого скрипта.

Проверка занимает три секунды:

ls -l /opt/kh-demo/run.sh
head -n 1 /opt/kh-demo/run.sh

Относительные пути и PATH

ExecStart=node server.js не работает, в том числе и с заданным WorkingDirectory=. Ошибка при загрузке юнита звучит как Neither a valid executable name nor an absolute path. Абсолютной должна быть только первая запись, аргументы после неё могут оставаться относительными. Правильно так: ExecStart=/usr/bin/node server.js, где server.js разрешается относительно WorkingDirectory. Правильный путь выдаёт command -v:

command -v bash

Осторожно с менеджерами версий: под nvm команда вернёт путь вроде /root/.nvm/versions/node/v22.14.0/bin/node, которого для пользователя службы не существует. Среды выполнения для служб ставят системно, например через NodeSource или Adoptium Temurin.

Отсутствующие переменные окружения

Классика жанра: при интерактивном входе программа работает, а как служба нет. Причина в том, что systemd не запускает login-шелл. Ни /etc/profile, ни ~/.bashrc, ни ~/.profile не читаются, и всё, что задано там через export, в службе отсутствует. PATH системной службы — это фиксированный минимальный путь, обычно /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin.

Поэтому переменные прописывают в сам юнит или в отдельный файл:

cat > /etc/kh-demo.env <<'EOF'
GREETING=привет
LANG=ru_RU.UTF-8
EOF
chmod 640 /etc/kh-demo.env
chown root:khdemo /etc/kh-demo.env
systemctl restart kh-demo.service
journalctl -u kh-demo.service -n 5 --no-pager

Если в выводе теперь стоит GREETING=привет вместо GREETING=не задано, файл дошёл. Проверить это можно и напрямую:

systemctl show kh-demo.service -p Environment -p EnvironmentFiles

Три ловушки в таких файлах: подстановка команд через $(...) не выполняется, буквальный знак $ нужно писать как $$, а кавычки попадают внутрь значения, где им не место.

Unit not found

Failed to start kh-demo.service: Unit kh-demo.service not found. почти всегда означает одно из трёх: файл лежит не в том каталоге, у него неверное расширение (.services вместо .service) или забыт daemon-reload. Первые два случая проясняет ls -l /etc/systemd/system/.

Различия между Debian 13, Debian 12, Ubuntu 24.04 и 22.04

Основы, то есть [Unit], [Service], [Install], Type=simple, Restart=, User= и WorkingDirectory=, на всех четырёх системах одинаковы. Различия начинаются на периферии.

Доступные директивы. В Ubuntu 22.04 идёт systemd 249, в Debian 12 systemd 252, в Ubuntu 24.04 systemd 255, в Debian 13 systemd 257. Всё, что появилось начиная с 253, на двух более старых системах отсутствует: Type=notify-reload (253), а также RestartSteps=, RestartMaxDelaySec= и RestartMode=direct (254). Ошибкой это не считается, директивы просто игнорируются. Так и появляются юниты, которые на одной системе делают задуманное, а на другой без видимой причины что-то другое.

systemctl --version

Сеть. Ubuntu Server использует netplan вместе с systemd-networkd, Debian в стандартной установке ifupdown. Из-за этого network-online.target на Ubuntu осмыслен без дополнительных действий, а на Debian только после включения ifupdown-wait-online.service.

Пути к интерпретаторам и средам выполнения. Здесь возникает большинство ошибок 203/EXEC при переносе юнита между системами. Node.js в Debian 13 идёт версии 20.19, в Debian 12 версии 18.20, в Ubuntu 24.04 версии 18.19, в Ubuntu 22.04 версии 12.22. PHP это 8.4 на Debian 13, 8.2 на Debian 12, 8.3 на Ubuntu 24.04 и 8.1 на Ubuntu 22.04. У Java разрыв самый большой: Debian 13 предлагает только openjdk-21-jre-headless, Debian 12 только openjdk-17-jre-headless, а Ubuntu 24.04 и 22.04 сразу 8, 11, 17 и 21. Кто скопирует ExecStart=/usr/lib/jvm/java-17-openjdk-amd64/bin/java с Debian 12 на Debian 13, гарантированно получит 203/EXEC.

Базы данных. Debian не поставляет mysql-server, там всегда работает MariaDB. Юнит с After=mysql.service ждёт на Debian службу, которой не существует, и стартует вообще без задержки, молча и без предупреждения. Правильно там After=mariadb.service.

Изменение, откат, уборка

Собственные юниты меняют прямо в файле плюс daemon-reload. Для юнитов из пакета берут вместо этого drop-in-файлы, чтобы следующее обновление не смело правку:

systemctl edit kh-demo.service

Команда создаёт /etc/systemd/system/kh-demo.service.d/override.conf, где остаются только изменённые директивы. Особый случай: списочные директивы вроде ExecStart= не заменяются, а дополняются. Чтобы такую директиву перезаписать, список сначала нужно очистить:

ExecStart=
ExecStart=/opt/kh-demo/run.sh --verbose

systemctl revert kh-demo.service убирает все drop-in-файлы обратно. Полный демонтаж примера, ровно в таком порядке:

systemctl disable --now kh-demo.service
rm -f /etc/systemd/system/kh-demo.service
rm -rf /etc/systemd/system/kh-demo.service.d
systemctl daemon-reload
systemctl reset-failed

Внимание на последнем шаге: systemctl reset-failed без аргумента сбрасывает состояние ошибки у всех юнитов, а не только у демонстрационной службы. На системе, где в состоянии failed висят и другие службы, из вывода systemctl list-units --state=failed пропадут и их сообщения. Кто хочет прибраться только здесь, берёт точечную форму systemctl reset-failed kh-demo.service из раздела выше.

Кто удалит файл, не вызвав перед этим disable, оставит после себя битую символьную ссылку в /etc/systemd/system/multi-user.target.wants/, которая будет всплывать предупреждением при каждом daemon-reload. Убирают её командой find /etc/systemd/system -xtype l -delete. Правда, этот вызов удалит любую битую ссылку внутри /etc/systemd/system, то есть и остатки чужих служб, которые, возможно, ещё нужны. Безопаснее сначала посмотреть без -delete или сразу взять точечный вариант find /etc/systemd/system -xtype l -name 'kh-demo*' -delete. По желанию можно убрать ещё пользователя и файлы:

userdel khdemo
rm -rf /opt/kh-demo /etc/kh-demo.env

Абсолютный путь в ExecStart, подходящий Type, отдельный пользователь, заданный WorkingDirectory, переменные через EnvironmentFile=: у кого всё это на месте, у того служба переживает перезагрузку и в случае сбоя сама рассказывает, что произошло.

Частые вопросы

Нужно ли выполнять systemctl daemon-reload после каждого изменения unit-файла?
Да. systemd держит unit-файлы в памяти, и правка на диске начинает действовать только после перезагрузки конфигурации. Если про неё забыть, при следующем systemctl status появится предупреждение «The unit file, source configuration file or drop-ins of ... changed on disk». Порядок всегда один: сначала systemctl daemon-reload, потом systemctl restart. Сам по себе reload ничего не перезапускает и на уже работающие процессы не влияет.
Что означает status=203/EXEC?
systemd не смог запустить программу, указанную в ExecStart. Причин может быть три: файла не существует (опечатка или не тот каталог), не выставлен бит исполнения, либо строка shebang скрипта указывает на несуществующий интерпретатор, например #!/usr/bin/python на Debian 12 и новее, где есть только /usr/bin/python3. Проверяют это командой ls -l по файлу и head -n 1 по его первой строке.
Почему служба не стартует после перезагрузки, хотя systemctl start работает?
Почти всегда не хватает секции [Install] с WantedBy=multi-user.target, либо systemctl enable вообще не выполнялся. Доказательством служит символьная ссылка: ls -l /etc/systemd/system/multi-user.target.wants/name.service должен показать файл, а systemctl is-enabled должен вывести «enabled». Если секции нет, enable прерывается с сообщением «The unit files have no installation config».
Type=simple или Type=forking?
Type=simple, если программа остаётся на переднем плане, что верно практически для всех современных приложений и является значением по умолчанию. Type=forking только тогда, когда программа обязательно уходит в фон сама, и в этом случае дополнительно нужен PIDFile=. У большинства демонов есть ключ вроде --foreground или --no-daemon, и с ним Type=simple лучший выбор. Неверная комбинация проявляется как «inactive (dead)» сразу после запуска или как тайм-аут через 90 секунд.
Почему служба не видит свои переменные окружения?
systemd не запускает login-шелл. Ни /etc/profile, ни ~/.bashrc, ни ~/.profile не читаются, и всё, что задано там через export, отсутствует. PATH это фиксированный минимальный путь. Поэтому переменные прописывают как Environment= в самом юните или в отдельный файл, который подключается через EnvironmentFile=. Там лежат чистые строки KEY=VALUE без export, подстановка команд не выполняется. Контроль: systemctl show name.service -p Environment.
Служба перестала реагировать на systemctl start, что делать?
Скорее всего сработал ограничитель запусков: по умолчанию разрешено пять попыток за десять секунд, после чего журнал сообщает «Start request repeated too quickly», а служба остаётся в состоянии failed. Команда systemctl reset-failed name.service сбрасывает счётчик. Внимание: ручные вызовы systemctl restart тоже считаются, поэтому при быстрой отладке ограничитель включают собственными руками.

systemd Linux Debian Ubuntu Администрирование серверов Автозапуск journalctl Unit-файл root-сервер