Linux #Linux #Ubuntu #Терминал #systemd #Сервер

systemd: свой сервис для скрипта и логи через journalctl

Как управлять службами через systemctl, написать свой сервис для скрипта или Python-бота с автозапуском и перезапуском, заменить cron таймером systemd и читать логи через journalctl. Команды, разбор вывода и частые ошибки.

13 минут
Содержание
  1. 1Что такое unit и где они лежат
  2. 2Управление службами: systemctl
  3. Запуск, остановка, перезапуск
  4. Автозапуск при загрузке
  5. Состояние службы
  6. Короткие проверки для скриптов
  7. Что сломалось
  8. 3Свой сервис для скрипта или Python-бота
  9. Файл юнита
  10. Проверка и запуск
  11. Сервис для обычного скрипта
  12. simple или oneshot
  13. Как поправить юнит, не создавая файл вручную
  14. 4Таймеры systemd вместо cron
  15. Как проверить расписание
  16. 5Пользовательские сервисы
  17. 6Логи: journalctl
  18. Основные ключи
  19. Сколько места занимает журнал
  20. 7Что тормозит загрузку: systemd-analyze
  21. 8Шпаргалка
  22. 9Частые ошибки
  23. 10Чек-лист
  24. 11Частые вопросы
  25. Чем start отличается от enable?
  26. Где лежат логи systemd?
  27. Как удалить свою службу?
  28. Почему systemctl пишет «System has not been booted with systemd as init system»?
  29. Что лучше: таймер systemd или cron?
  30. Можно ли запустить несколько копий одной службы?
  31. Как сделать, чтобы служба перезапускалась всегда, даже после нормального выхода?
  32. Нужно ли перезапускать службу после изменения скрипта?
  33. 12Что дальше

Рано или поздно на сервере появляется что-то своё: Telegram-бот на Python, скрипт резервного копирования, небольшой веб-сервис. Запустить его в терминале просто, но стоит закрыть SSH — и он умирает. После перезагрузки его нужно запускать руками, а если он упал ночью, никто об этом не узнает.

Всё это решает systemd — менеджер служб, который есть в Ubuntu, Debian, Fedora и почти во всех современных дистрибутивах. Он запускает программы при загрузке, перезапускает их после падения, собирает их вывод в единый журнал и умеет запускать задачи по расписанию вместо cron.

Ниже покажу, как управлять службами через systemctl, как написать свой сервис из десятка строк, как сделать таймер и как читать логи через journalctl. Все команды — для Ubuntu, но в других дистрибутивах с systemd они те же.

Что такое unit и где они лежат

В systemd всё, чем он управляет, называется unit (юнит). У каждого юнита есть тип — он виден по расширению файла:

  • .service — служба: программа или скрипт;
  • .timer — таймер, который запускает службу по расписанию;
  • .socket, .mount, .target и другие — сокеты, точки монтирования, группы юнитов.

Файлы юнитов лежат в трёх местах:

  • /usr/lib/systemd/system/ (он же /lib/systemd/system/) — юниты из пакетов. Руками их не правят: обновление пакета затрёт изменения.
  • /etc/systemd/system/ — ваши юниты и переопределения. Файл здесь имеет приоритет над одноимённым из /usr/lib.
  • ~/.config/systemd/user/ — пользовательские юниты, о них ниже.

Посмотреть, из какого файла загружен юнит и что в нём написано, можно командой:

Терминал
systemctl cat ssh.service

Первой строкой команда выводит путь к файлу, дальше — его содержимое вместе со всеми переопределениями.

Управление службами: systemctl

Чтобы запускать и останавливать службы, нужны права root, поэтому команды идут через sudo. Смотреть состояние можно и без него. Если с правами пока путаница, загляните в статью про root и sudo в Ubuntu.

Запуск, остановка, перезапуск

Терминал
sudo systemctl start nginx      # запустить
sudo systemctl stop nginx       # остановить
sudo systemctl restart nginx    # остановить и запустить заново
sudo systemctl reload nginx     # перечитать конфиг без остановки

reload поддерживают не все службы: веб-серверы умеют, а ваш скрипт — скорее всего нет. Если не уверены, используйте restart. Расширение .service в командах можно не писать — systemd подставит его сам.

Автозапуск при загрузке

start запускает службу только сейчас. Чтобы она стартовала после перезагрузки, её нужно включить:

Терминал
sudo systemctl enable nginx         # включить автозапуск
sudo systemctl disable nginx        # выключить автозапуск
sudo systemctl enable --now nginx   # включить и сразу запустить
sudo systemctl disable --now nginx  # выключить и сразу остановить

Ключ --now экономит одну команду и избавляет от частой ошибки: включили автозапуск, а запустить забыли — и до перезагрузки служба не работает.

enable создаёт символическую ссылку на файл юнита — это видно в выводе:

Пример вывода
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.

Состояние службы

Главная команда для диагностики:

Терминал
systemctl status nginx
Пример вывода
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Sun 2026-10-11 10:12:41 MSK; 2h 5min ago
       Docs: man:nginx(8)
   Main PID: 812 (nginx)
      Tasks: 5 (limit: 9387)
     Memory: 6.1M (peak: 7.4M)
        CPU: 143ms
     CGroup: /system.slice/nginx.service
             ├─812 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             └─813 "nginx: worker process"

окт 11 10:12:41 server systemd[1]: Starting nginx.service - A high performance web server...
окт 11 10:12:41 server systemd[1]: Started nginx.service - A high performance web server...

На что смотреть:

  • Loaded — путь к файлу юнита и enabled/disabled, то есть включён ли автозапуск;
  • Active — текущее состояние: active (running) — работает, inactive (dead) — остановлена, failed — упала, activating (auto-restart) — упала и ждёт перезапуска;
  • Main PID — номер главного процесса;
  • последние строки — хвост журнала службы. Чаще всего причина падения видна прямо здесь.

Если вывод не помещается, status открывает его в пейджере — выход по Q. Чтобы вывести всё сразу, добавьте --no-pager, а чтобы строки не обрезались по ширине терминала — -l.

Короткие проверки для скриптов

Для условий в скриптах удобнее команды, которые отвечают одним словом и кодом возврата:

Терминал
systemctl is-active nginx
systemctl is-enabled nginx
Пример вывода
active
enabled

Код возврата 0 означает «да». Поэтому можно писать так: systemctl is-active --quiet nginx || echo "nginx лежит". Ключ --quiet убирает вывод и оставляет только код.

Что сломалось

Список всех упавших юнитов:

Терминал
systemctl list-units --failed
Пример вывода
  UNIT          LOAD   ACTIVE SUB    DESCRIPTION
● mybot.service loaded failed failed Telegram-бот NixLife

1 loaded units listed.

Если список пуст — всё в порядке. После того как причину устранили, состояние failed сбрасывается командой sudo systemctl reset-failed mybot. Все запущенные службы показывает systemctl list-units --type=service, а все установленные файлы юнитов вместе с состоянием автозапуска — systemctl list-unit-files --type=service.

Свой сервис для скрипта или Python-бота

Допустим, у вас есть бот в /opt/mybot/bot.py с виртуальным окружением в /opt/mybot/venv, а токен лежит в файле /opt/mybot/.env. Запускать его будем от отдельного пользователя nixlife, а не от root.

Файл юнита

Создайте файл:

Терминал
sudo nano /etc/systemd/system/mybot.service

И вставьте:

Конфиг
[Unit]
Description=Telegram-бот NixLife
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=nixlife
WorkingDirectory=/opt/mybot
ExecStart=/opt/mybot/venv/bin/python /opt/mybot/bot.py
Restart=on-failure
RestartSec=5
EnvironmentFile=/opt/mybot/.env
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target

Разберу по строкам.

Секция [Unit] — описание и зависимости:

  • Description — название, которое видно в status и в журнале;
  • After=network-online.target — запускать после того, как сеть поднята. Без этого бот при загрузке может стартовать раньше сети и упасть на первом же запросе;
  • Wants=network-online.target — заодно попросить systemd дождаться сети. After задаёт только порядок, а Wants — саму зависимость, поэтому их ставят парой.

Секция [Service] — как запускать:

  • Type=simple — программа работает, пока работает служба. Это значение по умолчанию, подходит для ботов и большинства скриптов с бесконечным циклом;
  • User — от чьего имени запускать. Если не указать, служба работает от root;
  • WorkingDirectory — рабочий каталог. Относительные пути внутри скрипта (open("config.json")) будут считаться от него;
  • ExecStart — команда запуска. Путь к программе — полный. Python берём из виртуального окружения, тогда активировать его не нужно;
  • Restart=on-failure — перезапускать, если процесс завершился с ошибкой или был убит. Другие варианты: always — перезапускать всегда, даже после нормального выхода; no — никогда (по умолчанию);
  • RestartSec=5 — пауза перед перезапуском, чтобы при постоянной ошибке служба не перезапускалась без остановки;
  • EnvironmentFile — файл с переменными окружения в формате ИМЯ=значение, по одной на строку. Сюда кладут токены и пароли;
  • Environment — переменная прямо в юните. PYTHONUNBUFFERED=1 заставляет Python сразу отдавать print() в журнал, иначе вывод копится в буфере и появляется с большой задержкой.

Секция [Install] — куда «вешать» службу при enable. WantedBy=multi-user.target означает «при обычной загрузке системы». Для серверных служб это стандартное значение.

Проверка и запуск

Перед запуском файл стоит проверить:

Терминал
sudo systemd-analyze verify /etc/systemd/system/mybot.service

Если всё в порядке, команда ничего не выводит. Если есть ошибка — укажет строку. Вот реальный вывод для юнита, где ExecStart записан с относительным путём ./bot.py:

Вывод
mybot.service:10: Neither a valid executable name nor an absolute path: ./bot.py
mybot.service: Unit configuration has fatal error, unit will not be started.
Unit mybot.service has a bad unit file setting.

А так выглядит ошибка, когда программы по указанному пути нет — например, забыли создать виртуальное окружение:

Вывод
mybot.service: Command /opt/mybot/venv/bin/python is not executable: No such file or directory

Опечатку в значении verify тоже заметит. Здесь вместо on-failure написано sometimes:

Вывод
mybot.service:11: Failed to parse service restart specifier, ignoring: sometimes

Обратите внимание на слово ignoring: с такой опечаткой служба запустится, но перезапускаться не будет. Без проверки это легко пропустить.

Теперь главное — сообщить systemd о новом файле и запустить службу:

Терминал
sudo systemctl daemon-reload
sudo systemctl enable --now mybot
systemctl status mybot

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

После изменения кода самого бота daemon-reload не нужен — достаточно sudo systemctl restart mybot.

Сервис для обычного скрипта

Если это bash-скрипт, всё то же самое, только ExecStart указывает на сам скрипт:

Конфиг
ExecStart=/usr/local/bin/myscript.sh

У скрипта должны быть две вещи: первая строка-shebang #!/bin/bash и право на исполнение sudo chmod +x /usr/local/bin/myscript.sh. Без них служба упадёт с ошибкой 203/EXEC. Другой вариант — явно указать интерпретатор: ExecStart=/bin/bash /usr/local/bin/myscript.sh.

simple или oneshot

Тип службы зависит от того, как ведёт себя программа:

  • Type=simple — программа работает постоянно: бот, веб-сервер, цикл while true. Пока процесс жив, служба active (running).
  • Type=oneshot — программа делает дело и завершается: резервная копия, очистка, отправка отчёта. systemd ждёт её окончания и считает успехом код выхода 0. После этого служба переходит в inactive (dead) — и это нормально.

Если скрипт, который быстро завершается, оформить как simple с Restart=always, systemd будет перезапускать его снова и снова. Через несколько попыток сработает защита от зацикливания, и служба окажется в failed с пометкой start-limit-hit.

Как поправить юнит, не создавая файл вручную

Есть два удобных способа:

Терминал
sudo systemctl edit --full mybot   # открыть весь файл юнита в редакторе
sudo systemctl edit nginx          # создать переопределение (drop-in)

Второй вариант нужен для юнитов из пакетов: он создаёт /etc/systemd/system/nginx.service.d/override.conf, куда вы пишете только изменённые строки. Обновление пакета их не затрёт. После systemctl edit делать daemon-reload не нужно — команда выполнит его сама.

Таймеры systemd вместо cron

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

Нужно два файла. Служба /etc/systemd/system/backup.service:

Конфиг
[Unit]
Description=Резервная копия /srv/data

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

И таймер с тем же именем /etc/systemd/system/backup.timer:

Конфиг
[Unit]
Description=Ежедневный запуск резервной копии

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=10min

[Install]
WantedBy=timers.target
  • OnCalendar — расписание в формате день_недели год-месяц-день часы:минуты:секунды, звёздочка означает «любой»;
  • Persistent=true — если в 03:00 компьютер был выключен, задача выполнится сразу после включения;
  • RandomizedDelaySec — случайная задержка до 10 минут, чтобы несколько задач не стартовали в одну секунду.

У службы нет секции [Install]: включать её не нужно, её запускает таймер. Включают именно таймер:

Терминал
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer

Оба файла можно проверить одной командой — systemd-analyze verify backup.timer backup.service. Список всех таймеров:

Терминал
systemctl list-timers
Пример вывода
NEXT                           LEFT LAST                           PASSED UNIT          ACTIVATES
Mon 2026-10-12 03:04:12 MSK     7h  Sun 2026-10-11 03:07:55 MSK   16h ago backup.timer  backup.service
Mon 2026-10-12 06:41:30 MSK    10h  Sun 2026-10-11 06:12:01 MSK   13h ago apt-daily-upgrade.timer apt-daily-upgrade.service

2 timers listed.

Колонки NEXT и LAST — следующий и последний запуск. Добавьте --all, чтобы увидеть и неактивные таймеры. Чтобы запустить задачу прямо сейчас, не дожидаясь расписания, запустите службу: sudo systemctl start backup.service.

Как проверить расписание

Формат OnCalendar непривычен, но его не нужно угадывать: systemd-analyze calendar покажет, когда выражение сработает. Эта команда работает даже там, где systemd не запущен. Реальный вывод:

Терминал
systemd-analyze calendar "Mon..Fri 09:00" "*:0/15" daily
Вывод
  Original form: Mon..Fri 09:00
Normalized form: Mon..Fri *-*-* 09:00:00
    Next elapse: Mon 2026-10-12 09:00:00 MSK
       (in UTC): Mon 2026-10-12 06:00:00 UTC
       From now: 13h left

  Original form: *:0/15
Normalized form: *-*-* *:00/15:00
    Next elapse: Sun 2026-10-11 20:00:00 MSK
       (in UTC): Sun 2026-10-11 17:00:00 UTC
       From now: 2min 45s left

  Original form: daily
Normalized form: *-*-* 00:00:00
    Next elapse: Mon 2026-10-12 00:00:00 MSK
       (in UTC): Sun 2026-10-11 21:00:00 UTC
       From now: 4h 2min left

Normalized form — как systemd понял выражение, Next elapse — ближайшее срабатывание. Чтобы увидеть несколько следующих запусков, добавьте --iterations=3:

Терминал
systemd-analyze calendar --iterations=3 "Sun *-*-* 04:30"
Вывод
  Original form: Sun *-*-* 04:30
Normalized form: Sun *-*-* 04:30:00
    Next elapse: Sun 2026-10-18 04:30:00 MSK
       (in UTC): Sun 2026-10-18 01:30:00 UTC
       From now: 6 days left
   Iteration #2: Sun 2026-10-25 04:30:00 MSK
       (in UTC): Sun 2026-10-25 01:30:00 UTC
       From now: 1 week 6 days left
   Iteration #3: Sun 2026-11-01 04:30:00 MSK
       (in UTC): Sun 2026-11-01 01:30:00 UTC
       From now: 2 weeks 6 days left

Несколько готовых выражений: hourly — каждый час, daily — в полночь, weekly — в понедельник в полночь, *:0/15 — каждые 15 минут, Mon..Fri 09:00 — по будням в 9 утра, *-*-01 02:00 — первого числа каждого месяца.

Пользовательские сервисы

Если у вас нет root или служба нужна только вам, её можно запустить в пользовательском экземпляре systemd. Файл кладут в ~/.config/systemd/user/, а в командах добавляют --user — без sudo:

Терминал
mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/mybot.service
systemctl --user daemon-reload
systemctl --user enable --now mybot
journalctl --user -u mybot -f

Два отличия от системных служб:

  • строка User= не нужна — служба и так работает от вас;
  • в [Install] пишут WantedBy=default.target, а не multi-user.target.

Есть важная тонкость. По умолчанию пользовательские службы запускаются при входе в систему и останавливаются, когда вы выходите из последнего сеанса — например, закрываете SSH. Чтобы они работали всегда и стартовали при загрузке без входа, включите «задержку» (linger):

Терминал
sudo loginctl enable-linger nixlife

Проверить можно командой loginctl show-user nixlife --property=Linger — должно быть Linger=yes.

Логи: journalctl

Всё, что служба пишет в стандартный вывод и в поток ошибок, systemd сохраняет в журнал. print() в Python, echo в bash — всё оказывается там с отметкой времени. Читается журнал командой journalctl. Свои логи пользователь видит и так, а чтобы читать журналы системных служб, нужен sudo или членство в группе adm.

Основные ключи

Терминал
journalctl -u mybot                    # журнал одной службы
journalctl -u mybot -f                 # следить в реальном времени
journalctl -u mybot -n 50              # последние 50 строк
journalctl -u mybot -b                 # только с последней загрузки
journalctl -u mybot -b -1              # за предыдущую загрузку
journalctl -u mybot --since "1 hour ago"
journalctl -u mybot --since today
journalctl --since "2026-10-11 09:00" --until "2026-10-11 10:00"
journalctl -p err -b                   # только ошибки с последней загрузки
journalctl -u mybot -o cat --no-pager  # чистый текст без пейджера

Что делает каждый ключ:

  • -u — фильтр по юниту. Можно указать несколько: -u nginx -u mybot;
  • -f — показывать новые строки по мере появления, как tail -f. Выход — Ctrl + C;
  • -n — сколько последних строк вывести;
  • -b — с текущей загрузки, -b -1 — с предыдущей. Список загрузок — journalctl --list-boots. Это работает, только если журнал хранится на диске; в Ubuntu так и есть по умолчанию;
  • --since и --until — интервал. Понимают today, yesterday, "1 hour ago", "30 min ago" и точные даты;
  • -p — минимальный уровень важности: emerg, alert, crit, err, warning, notice, info, debug. -p err покажет ошибки и всё, что серьёзнее;
  • -o cat — только текст сообщений, без даты, хоста и имени процесса. Удобно, чтобы скопировать вывод бота или передать его в grep;
  • --no-pager — вывести всё сразу, без пролистывания. Нужен в скриптах;
  • -e — открыть журнал сразу в конце, -r — показать новые записи первыми.

Обычный вывод выглядит так:

Терминал
journalctl -u mybot -n 5
Пример вывода
окт 11 14:02:10 server systemd[1]: Started mybot.service - Telegram-бот NixLife.
окт 11 14:02:11 server python[2741]: Бот запущен, ожидаю сообщения
окт 11 14:20:37 server python[2741]: Traceback (most recent call last):
окт 11 14:20:37 server python[2741]:   File "/opt/mybot/bot.py", line 42, in <module>
окт 11 14:20:37 server systemd[1]: mybot.service: Main process exited, code=exited, status=1/FAILURE

Слева дата, имя хоста, процесс и его PID, справа — сообщение. Строки от systemd[1] — это события самой службы: запуск, падение, перезапуск. По ним видно, с каким кодом процесс завершился.

Сколько места занимает журнал

Терминал
journalctl --disk-usage
Пример вывода
Archived and active journals take 312.5M in the file system.

По умолчанию журнал занимает не больше 10% файловой системы и не больше 4 ГБ. Освободить место можно вручную:

Терминал
sudo journalctl --vacuum-size=200M   # оставить не больше 200 МБ
sudo journalctl --vacuum-time=2weeks # удалить записи старше двух недель

Чистятся только архивные файлы журнала, текущий не трогается, поэтому итоговый размер может оказаться чуть больше заданного. Чтобы ограничение действовало постоянно, откройте /etc/systemd/journald.conf, раскомментируйте строку SystemMaxUse= и задайте, например, SystemMaxUse=500M. Затем перезапустите журнал командой sudo systemctl restart systemd-journald. Если место на диске заканчивается и непонятно, куда оно ушло, поможет статья про свободное место на диске в Linux.

Что тормозит загрузку: systemd-analyze

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

Терминал
systemd-analyze
Пример вывода
Startup finished in 4.112s (firmware) + 2.031s (loader) + 1.874s (kernel) + 9.640s (userspace) = 17.658s
graphical.target reached after 9.581s in userspace.

Самые долгие службы — первыми:

Терминал
systemd-analyze blame | head
Пример вывода
6.012s systemd-networkd-wait-online.service
1.204s snapd.seeded.service
  843ms apt-daily-upgrade.service
  512ms dev-sda2.device
  318ms systemd-journal-flush.service
  247ms nginx.service

Цифры показывают, сколько стартовала каждая служба, но не то, сколько из-за неё ждала система — многие запускаются одновременно. Цепочку, которая реально задерживает загрузку, показывает systemd-analyze critical-chain. Частый лидер на серверах — systemd-networkd-wait-online.service: она ждёт, пока поднимутся все сетевые интерфейсы, и если какой-то из них не подключён, висит до таймаута.

Шпаргалка

КомандаЧто делает
systemctl status имяСостояние службы и хвост журнала
sudo systemctl start / stop / restart имяЗапустить, остановить, перезапустить
sudo systemctl enable --now имяВключить автозапуск и сразу запустить
sudo systemctl disable --now имяВыключить автозапуск и остановить
systemctl is-active / is-enabled имяКороткий ответ для скриптов
systemctl list-units --failedВсе упавшие юниты
systemctl cat имяПоказать файл юнита
sudo systemctl edit --full имяОтредактировать юнит
sudo systemctl daemon-reloadПеречитать файлы после правки
systemd-analyze verify файлПроверить юнит на ошибки
systemctl list-timersТаймеры: последний и следующий запуск
systemd-analyze calendar "выражение"Проверить расписание OnCalendar
journalctl -u имя -fЖурнал службы в реальном времени
journalctl -p err -bОшибки с последней загрузки
journalctl --since "1 hour ago"Записи за последний час
sudo journalctl --vacuum-size=200MУжать журнал
systemd-analyze blameЧто дольше всего стартует

Частые ошибки

Если служба не запускается, начните с systemctl status имя и journalctl -u имя -n 50. В строке Main process exited будет код, по которому почти всегда понятна причина.

Относительные пути. ExecStart=python3 bot.py или ExecStart=./bot.py — плохая идея. Имя программы без пути systemd ищет в стандартных каталогах и найдёт системный Python, а не тот, что в виртуальном окружении. Путь с ./ или ~ он вообще не примет. Пишите полные пути и к интерпретатору, и к скрипту. Внутри скрипта относительные пути считаются от WorkingDirectory, а если она не задана — от корня /.

Нет прав. Служба работает от пользователя из User=, а не от вас. Если ему не хватает прав на чтение файлов или запись в каталог, будет Permission denied в журнале. Если такого пользователя нет вовсе — код 217/USER, если не существует WorkingDirectory — 200/CHDIR.

Забыли daemon-reload. Поправили файл, сделали restart, а ничего не изменилось — systemd работает со старой версией. После любой правки юнита: sudo systemctl daemon-reload, потом restart.

Скрипт без shebang или без права на исполнение. Код 203/EXEC и в журнале Exec format error или Permission denied. Добавьте в первую строку #!/bin/bash (или #!/usr/bin/env python3) и выполните chmod +x. systemd-analyze verify такую проблему ловит заранее: Command /usr/local/bin/myscript.sh is not executable: Permission denied.

Служба сразу выходит. Если скрипт отработал и завершился, а status показывает inactive (dead) — для Type=simple это значит, что программа закончилась. Для разовых задач поставьте Type=oneshot. Если же программа должна работать постоянно, ищите причину выхода в журнале. Ещё один вариант — скрипт сам уходит в фон через & или nohup. В службе так делать не нужно: systemd сам держит процесс.

Бот молчит в журнале. Python буферизует вывод, когда пишет не в терминал. Добавьте Environment=PYTHONUNBUFFERED=1 или запускайте с ключом -u.

Нет переменных окружения. У службы нет вашего ~/.bashrc и переменных из сеанса. Всё нужное передавайте через Environment= или EnvironmentFile=.

Чек-лист

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

Чем start отличается от enable?

start запускает службу сейчас и до перезагрузки. enable включает автозапуск при загрузке, но сам по себе службу не запускает. Чтобы сделать и то и другое, используйте enable --now.

Где лежат логи systemd?

В двоичных файлах журнала в /var/log/journal/. Читать их нужно через journalctl, а не текстовым редактором. Если этого каталога нет, журнал хранится только в памяти в /run/log/journal/ и пропадает при перезагрузке.

Как удалить свою службу?

Остановите и выключите её: sudo systemctl disable --now mybot. Затем удалите файл /etc/systemd/system/mybot.service, выполните sudo systemctl daemon-reload и при необходимости sudo systemctl reset-failed.

Почему systemctl пишет «System has not been booted with systemd as init system»?

Вы внутри контейнера Docker или WSL, где systemd не запущен как первый процесс. Службы там запускают по-другому — средствами самого контейнера. На обычном сервере и в виртуальной машине этой ошибки нет. Например, в LXC-контейнерах Proxmox systemd работает как обычно.

Что лучше: таймер systemd или cron?

Для серверных задач — таймер: логи в журнале, видно последний и следующий запуск, есть Persistent=true. Для одной простой строки быстрее cron, его выражения удобно собирать в генераторе cron.

Можно ли запустить несколько копий одной службы?

Да, через шаблоны: файл называется mybot@.service, а внутри вместо изменяемой части пишут %i. Запуск — systemctl start mybot@first mybot@second, и %i заменится на first и second.

Как сделать, чтобы служба перезапускалась всегда, даже после нормального выхода?

Поставьте Restart=always. Но если программа выходит сразу после запуска, systemd после нескольких быстрых перезапусков остановится с ошибкой start-limit-hit — сначала разберитесь, почему она выходит.

Нужно ли перезапускать службу после изменения скрипта?

Да: sudo systemctl restart имя. Запущенный процесс продолжает работать со старым кодом. А daemon-reload нужен только при правке самого файла .service.

Что дальше

Свой сервис — обычно следующий шаг после установки Ubuntu Server и базовой защиты. Если сервис нужно держать запущенным в терминале только на время отладки, удобнее tmux: сессия переживёт отключение SSH, а когда всё заработает, остаётся перенести запуск в systemd.

Статья помогла?

Комментарии

Пока никто не написал. Будьте первым — вопрос или свой опыт одинаково полезны.

Комментарий появится после проверки. Ссылки и реклама не публикуются.