Веб-сервер, база данных, Docker и SSH работают на сервере постоянно, сами запускаются после перезагрузки и поднимаются снова, если упали. За это отвечает systemd, а управляют им командой systemctl. В этой статье разберём, как устроены службы, научимся запускать, останавливать и перезапускать их, смотреть статус и логи, а затем сделаем собственную службу для скрипта или бота, чтобы он работал круглосуточно без открытого терминала. Все команды мы проверили на тестовом сервере с Debian 12.
Как systemd управляет службами
Когда сервер включается, ядро Linux запускает самый первый процесс с номером 1. В Debian, Ubuntu и большинстве современных дистрибутивов этим процессом является systemd. Его задача в том, чтобы запустить всё остальное в правильном порядке, а затем следить за запущенным до самого выключения сервера. Всё, чем управляет systemd, описано в небольших текстовых файлах, которые называются юнитами. Юнит службы имеет расширение .service и сообщает, какую программу запустить, от имени какого пользователя, после каких других служб и что делать, если программа завершилась.
Запустив службу, systemd помещает её процесс и все процессы, которые он породит, в отдельную группу. Благодаря этому система точно знает, какие процессы относятся к службе. Команда остановки завершает их все, а не только главный, и в статусе службы видно, сколько памяти и процессорного времени расходует вся группа целиком. Всё, что программа выводит в консоль, systemd не теряет, а передаёт в системный журнал, из которого его затем можно прочитать командой journalctl. Именно поэтому программе, запущенной как служба, не нужен открытый терминал. Она не зависит от того, подключены вы по SSH или нет.
Здесь важно различать два независимых действия. Запустить службу значит выполнить её прямо сейчас. Включить службу значит сделать так, чтобы systemd запускал её при каждой загрузке сервера. Можно запустить службу, не включая её, и тогда после перезагрузки она не поднимется. Можно включить её, не запуская, и тогда она заработает только после следующей загрузки. На практике чаще всего нужны оба действия сразу, и для этого есть параметр --now.
Последнее, что стоит понимать, это перезапуск после сбоя. Если в юните указано Restart=always, systemd замечает, что процесс завершился, выжидает заданную паузу и запускает его снова. Мы проверили это на тестовом сервере. Процесс нашей службы был принудительно завершён, и через пять секунд, как и было задано в настройках, systemd поднял его заново.
1. Посмотрите статус службы
Статус показывает, работает ли служба, включён ли её автозапуск, главный процесс, расход памяти и последние строки лога.
systemctl status nginx
Служба nginx работает и включена в автозапуск
Самое важное здесь в первых строках.
- Loaded показывает, где лежит файл юнита, и после
;состояние автозапуска,enabledилиdisabled. - Active показывает текущее состояние.
active (running)значит, что служба работает,inactive (dead)что остановлена, аfailedчто она упала. - Main PID это номер главного процесса.
Если вывод не помещается на экран, systemctl открывает его в программе просмотра, выход из неё по клавише q.
2. Управляйте службой
systemctl start nginx # запустить
systemctl stop nginx # остановить
systemctl restart nginx # перезапустить
systemctl reload nginx # перечитать настройки без остановки, если служба это умеет
systemctl enable nginx # включить автозапуск
systemctl disable nginx # выключить автозапуск
systemctl enable --now nginx # включить автозапуск и сразу запуститьПроверить отдельно, работает ли служба и включена ли она, удобно короткими командами. Они пригодятся и в скриптах.
systemctl is-active nginx
systemctl is-enabled nginxСписок всех работающих служб и тех, что завершились с ошибкой, покажут две команды.
systemctl list-units --type=service --state=running
systemctl --failed3. Посмотрите логи службы
Всё, что служба пишет в консоль, хранится в журнале systemd.
# Последние 50 строк лога службы
journalctl -u nginx -n 50
# Следить за логом в реальном времени, выход по Ctrl+C
journalctl -u nginx -f
# Записи за последний час
journalctl -u nginx --since "1 hour ago"4. Создайте свою службу
Допустим, у вас есть скрипт на Python, например Telegram-бот, который должен работать круглосуточно. Если запустить его в терминале, он остановится, как только вы отключитесь от сервера. Сделаем для него службу.
Создайте для бота отдельного пользователя без права входа на сервер. Так бот не получит лишних прав, даже если в нём найдётся уязвимость.
bashuseradd -r -s /usr/sbin/nologin appuserПоложите скрипт в папку, например
/opt/mybot/bot.py, и сделайте этого пользователя её владельцем.bashchown -R appuser:appuser /opt/mybotСоздайте файл юнита.
bashnano /etc/systemd/system/mybot.serviceВставьте в него следующее содержимое и сохраните файл сочетанием Ctrl + O, затем Enter и Ctrl + X.
ini[Unit] Description=My Telegram bot After=network-online.target Wants=network-online.target [Service] User=appuser WorkingDirectory=/opt/mybot ExecStart=/usr/bin/python3 /opt/mybot/bot.py Restart=always RestartSec=5 [Install] WantedBy=multi-user.targetСообщите systemd о новом файле, включите и запустите службу, затем проверьте статус.
bashsystemctl daemon-reload systemctl enable --now mybot systemctl status mybot

Своя служба запущена, вывод скрипта уже виден в логе
Разберём, что означают строки юнита.
- After и Wants со значением
network-online.targetоткладывают запуск до тех пор, пока на сервере не заработает сеть. Боту без сети делать нечего. - User задаёт пользователя, от имени которого работает скрипт.
- WorkingDirectory это папка, в которой скрипт будет искать свои файлы.
- ExecStart это команда запуска. Путь к программе указывается полностью, например
/usr/bin/python3. Узнать его можно командойwhich python3. - Restart=always и RestartSec=5 перезапускают скрипт через 5 секунд после любого завершения.
- WantedBy=multi-user.target нужен для автозапуска при загрузке сервера.
Чтобы убедиться, что автоперезапуск работает, мы принудительно завершили процесс бота. В логе видно, как systemd зафиксировал падение и через пять секунд запустил бота снова.

Процесс убит в 12:04:50 и снова работает в 12:04:55
После каждого изменения юнита
Если вы поправили файл .service, выполните systemctl daemon-reload, а затем systemctl restart mybot. Без первой команды systemd продолжит пользоваться старой версией файла. Проверить юнит на ошибки можно командой systemd-analyze verify /etc/systemd/system/mybot.service.
Частые вопросы
status=203/EXEC
systemd не смог запустить программу из строки ExecStart. Мы получили эту ошибку, указав несуществующий путь /usr/bin/python вместо /usr/bin/python3. Проверьте путь командой which, а если запускаете свой скрипт напрямую, проверьте, что у него есть право на выполнение и первая строка вида #!/usr/bin/env python3.

Неверный путь к программе в ExecStart
Unit mybot.service could not be found
systemd не знает о файле. Проверьте, что он лежит в /etc/systemd/system/ и называется так же, как вы пишете в команде, а после создания файла выполните systemctl daemon-reload.
Служба постоянно перезапускается
Программа запускается и сразу падает, а Restart=always поднимает её снова. Причину ищите в логе командой journalctl -u mybot -n 50. Чаще всего это ошибка в самом скрипте, отсутствующая библиотека или нехватка прав на файлы. Про права подробно рассказано в статье про chmod и chown.
Как полностью удалить свою службу
systemctl disable --now mybot
rm /etc/systemd/system/mybot.service
systemctl daemon-reloadНаши ресурсы
Telegram-канал: https://t.me/f3cloud_news
Официальный сайт: https://f3cloud.com