Сервис не запускается
Сервис в systemd отказывается запускаться, падает через пару секунд после старта или уходит в бесконечный цикл перезапуска — одна из самых частых проблем на VPS. Причина почти всегда одна из пяти: занят порт, ошибка в конфигурации, не хватает прав, не готова зависимость или сервису не хватило памяти, — и в этой статье разобран общий метод диагностики через systemctl status и journalctl, а затем каждая из пяти причин на конкретных примерах.
Что понадобится
- VPS MHost на Ubuntu 22.04/24.04, Debian 11/12 или AlmaLinux/Rocky Linux 8/9.
- Доступ по SSH под
rootили пользователем сsudo— если ещё не подключались к серверу, начните со статьи «Консоль VNC и первое подключение (SSH/RDP)». - Имя сервиса, который не запускается: например
nginx,postgresql,myapp. Список всех systemd-сервисов на сервере —systemctl list-units --type=service --all.
Шаг 1. Проверьте статус: systemctl status
systemctl status <service>Пример вывода для упавшего сервиса:
● myapp.service - My Application
Loaded: loaded (/etc/systemd/system/myapp.service; enabled; preset: enabled)
Active: failed (Result: exit-code) since Fri 2026-07-10 09:14:22 UTC; 38s ago
Process: 4021 ExecStart=/opt/myapp/venv/bin/python /opt/myapp/main.py (code=exited, status=1/FAILURE)
Main PID: 4021 (code=exited, status=1/FAILURE)Loaded заодно подтверждает, что systemd вообще знает такой юнит и откуда взял его файл; строка вида Unit myapp.service could not be found означает опечатку в имени, неустановленный пакет либо новый unit-файл, про который systemd ещё не знает (лечится командой systemctl daemon-reload).
Главное — строка Active:
- `active (running)` — сервис работает; если вы здесь по этой статье, скорее всего, вы видите не это.
- `activating (auto-restart)` — сервис падает и systemd раз за разом пытается перезапустить его автоматически (настройка
Restart=в юните). Загляните в журнал (Шаг 2), не дожидаясь, пока попытки закончатся. - `failed` — не смог подняться либо упал и повторно не перезапускается.
- `inactive (dead)` — просто остановлен, никто и не пытался его запустить: возможно, вы просто забыли его включить —
systemctl start <service>.
Строка Process/Main PID с кодом в скобках расшифровывается так:
- `code=exited, status=N/...` (N ≠ 0) — процесс успешно стартовал и сам завершился с ошибкой — причину ищите в собственном выводе приложения (Шаг 2).
- `code=exited, status=203/EXEC` — до запуска самого приложения дело не дошло: systemd не смог выполнить файл из
ExecStart— неверный путь, файл не помечен исполняемым или недоступен по правам. См. «Неправильные права доступа» и «Не хватает зависимости» ниже. - `code=killed, signal=KILL` — процесс убит сигналом SIGKILL. Чаще всего это OOM killer ядра — см. «Не хватило памяти» ниже.
- `code=killed, signal=SEGV` (или
ABRT) — процесс упал сам: похоже на баг в приложении или несовместимую версию библиотеки. - `Result: oom-kill` — на более новых версиях systemd так отдельно помечается случай, когда именно cgroup сервиса получила уведомление об уничтожении процесса от OOM killer'а ядра — по сути то же самое, что
signal=KILLиз-за нехватки памяти, но systemd явно называет причину.
Шаг 2. Прочитайте журнал: journalctl
journalctl -u <service> -e-u — фильтр по конкретному юниту; -e сразу перематывает вывод в конец, к самым свежим записям.
Полезные варианты команды:
journalctl -xeu <service>— то же самое плюс флаг-x: там, где это возможно, добавляет пояснение к системному сообщению из каталога systemd. Именно эту команду обычно подсказывает самsystemctl, когда сервис падает.journalctl -u <service> --no-pager -n 50— последние 50 строк без интерактивного пейджера, удобно, если нужно скопировать вывод целиком.journalctl -u <service> -f— следить за журналом в реальном времени, пригодится сразу после перезапуска (Шаг 4).journalctl -u <service> --since "10 min ago"— только записи за последние 10 минут, если журнал юнита большой.
Шаг 3. Найдите причину среди самых частых
Строка с ошибкой из Шага 2 почти всегда прямо указывает на одну из пяти причин ниже.
Порт уже занят
Если порт, который пытается слушать сервис, уже занят другим процессом, забиндиться на него не получится, и сервис упадёт сразу при старте — в журнале это обычно видно как что-то вроде:
bind() to 0.0.0.0:80 failed (98: Address already in use)Найдите, кто реально держит порт:
ss -tlnp | grep :80-t— только TCP;-l— только слушающие сокеты;-n— порты и адреса числом, без резолва в имена;-p— какой процесс держит порт (нуженsudo/root, иначе имя процесса и PID скрыты).
Пример вывода:
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("apache2",pid=1587,fd=6))Порт 80 занят процессом apache2 — именно поэтому второй сервис (например, nginx) не смог стартовать. Дальше — выбор:
- если второй сервис реально не нужен — остановите и отключите его автозапуск:
systemctl disable --now apache2; - если нужны оба — смените порт у того сервиса, который настраиваете сейчас (для Nginx это директива
listenв конфигурации vhost, см. статью «Nginx: установка и первый сайт»), и не забудьте открыть новый порт в файрволе.
Ошибка в конфигурации
Большинство зрелых демонов умеют проверить свой конфигурационный файл на синтаксис без реального запуска — пользуйтесь этим перед restart/reload, чтобы не уронить рабочий процесс проверкой битого конфига:
nginx -t
apachectl configtest
sshd -tЕсли у сервиса нет отдельного флага проверки, самый надёжный способ найти ошибку — всё тот же journalctl (Шаг 2): перед тем как завершиться, приложение почти всегда само печатает, в каком файле и на какой строке оно споткнулось.
Отдельно от конфигурации самого приложения можно проверить и unit-файл systemd — на опечатки в директивах, циклы зависимостей и подобное:
systemd-analyze verify <service>Если проблем нет, команда ничего не выводит; при ошибке — печатает предупреждение с указанием файла и директивы.
Неправильные права доступа
Юнит-файл сервиса может явно задавать, от имени какого пользователя реально работает процесс, — не обязательно root, даже если вы управляете сервисом через sudo. Узнать, от чьего имени запускается сервис:
systemctl show <service> -p User -p GroupПустое значение (User= без содержимого) значит, что сервис по умолчанию работает от root. Если указан отдельный пользователь — именно у него должны быть права на чтение (а иногда и запись) конфигурации, данных и рабочего каталога сервиса.
Проверьте владельца и права:
ls -l /path/to/config
namei -l /path/to/confignamei -l построчно показывает права и владельца для каждого каталога в цепочке пути — от корня до самого файла, а не только для конечного файла. Это удобно, когда сам файл выглядит доступным (ls -l в порядке), а прочитать его всё равно не получается: значит, доступ закрыт на каком-то из родительских каталогов.
Исправьте владельца и права на нужные (подставьте пользователя и группу, которые вернула команда systemctl show из примера выше, — www-data здесь лишь пример для Ubuntu/Debian):
chown www-data:www-data /path/to/config
chmod 640 /path/to/configausearch -m avc -ts recentЕсли команда не найдена, установите пакет audit (dnf install audit) и включите systemctl enable --now auditd. Пример восстановления SELinux-контекста через semanage fcontext и restorecon для каталога вне стандартных путей — в статье «Nginx: установка и первый сайт».
Не хватает зависимости
Бывает двух видов.
Первый — сервис стартует раньше, чем готово то, от чего он зависит: например, приложение подключается к базе данных сразу при старте, а сама база на этом же сервере ещё не успела подняться после перезагрузки. Характерный симптом: вручную, спустя минуту после reboot, сервис запускается без проблем, а сразу при загрузке сервера — падает. Чинится через systemd-зависимости — переопределите юнит:
systemctl edit <service>Команда откроет редактор и сама сохранит правки в отдельный override-файл (/etc/systemd/system/<service>.service.d/override.conf), автоматически перечитав конфигурацию systemd после сохранения — отдельный daemon-reload не нужен. Добавьте:
[Unit]
After=postgresql.service
Requires=postgresql.service(замените postgresql.service на реальное имя юнита, от которого зависит ваш сервис). After задаёт только порядок запуска, но не гарантирует, что зависимость вообще поднялась; Requires добавляет к этому жёсткую зависимость — если postgresql.service не запустится, systemd не станет пытаться запускать и ваш сервис (в отличие от более мягкого Wants=, который просто игнорирует неудачу зависимости).
Второй вид — не хватает чего-то на уровне самой системы: не установлена библиотека, не создано виртуальное окружение Python, не выполнен npm install. В журнале это обычно видно как явная ошибка самого приложения — что-то вроде ModuleNotFoundError: No module named 'foo' (Python) или Error: Cannot find module 'bar' (Node.js), — либо тот же status=203/EXEC, если файла из ExecStart не существует вовсе (например, неверно указан путь до venv). Проверьте отсутствующие библиотеки:
ldd $(which <binary>)Строки => not found в выводе показывают, каких .so-библиотек не хватает, — доустановите нужный пакет.
Не хватило памяти (OOM)
Если сервис (сам или вместе с остальными процессами на сервере) исчерпал всю доступную память, ядро Linux принудительно убивает один из процессов, чтобы не допустить полного зависания сервера, — это и есть OOM killer (out-of-memory killer). Убитый процесс — не обязательно самый прожорливый: ядро выбирает по эвристике (oom_score), но чаще всего это действительно тот, кто съел больше всего.
Симптом в systemctl status — падение без внятной причины со стороны самого приложения:
Main PID: 4021 (code=killed, signal=KILL)или, отдельным статусом:
Active: failed (Result: oom-kill)Подтвердите, что дело именно в OOM killer'е, а не в ручном kill или падении самого приложения:
dmesg -T | grep -i -E "out of memory|killed process"
journalctl -k -e-k показывает только сообщения ядра (то же самое, что dmesg, но через журнал); -T у dmesg печатает время в человекочитаемом формате вместо секунд с загрузки. Типичная строка в выводе:
Out of memory: Killed process 4021 (myapp) total-vm:2048000kB, anon-rss:1523000kB, file-rss:0kBПроверьте общую картину по памяти и swap:
free -hЧто делать:
- быстро — добавьте swap, если его вообще нет:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
swapon --show- надёжнее — снизьте реальное потребление памяти сервисом: меньше воркеров/потоков, ниже лимиты кеша в его собственной конфигурации (зависит от конкретного приложения);
- поставьте сервису мягкий потолок через systemd, чтобы утечка памяти укладывала только его cgroup, а не весь сервер:
[Service]
MemoryMax=512M(добавляется тем же systemctl edit <service>, что и в предыдущей причине);
- если памяти физически не хватает на тарифе — увеличьте объём RAM в биллинг-панели my.mhost.ee.
Шаг 4. Исправьте причину и перезапустите
Если правили сам unit-файл вручную (а не через systemctl edit), обязательно выполните daemon-reload перед перезапуском — иначе systemd продолжит использовать старую версию, уже загруженную в память:
systemctl daemon-reload
systemctl restart <service>
systemctl status <service>Не полагайтесь только на active (running) сразу после команды — задержитесь на несколько секунд и последите за журналом, чтобы поймать сервис, который падает не мгновенно, а спустя пару секунд после старта (например, при исчерпании памяти под нагрузкой):
journalctl -u <service> -f(Ctrl+C, чтобы выйти из режима слежения)
Что дальше
Если чинили именно веб-сервер и после перезапуска сам сервис в порядке, а сайт всё равно не открывается снаружи, — это уже отдельная тема, разобранная в статье «Сайт не открывается», включая проверку файрвола (см. также «Файрвол ufw: порты»). Если journalctl вместо перечисленных выше причин жалуется на нехватку места на диске («No space left on device») — см. статью «Диск заполнен»; если сам сервис в порядке, а сервер в целом тормозит, — статью «Высокая нагрузка на сервер». Если ничего не помогло, а незадолго до этого вы меняли конфигурацию, — проще откатить именно то изменение (или восстановить конфигурацию из бэкапа, если он есть), чем разбираться дальше.
Диагностика, специфичная для конкретного приложения, — в статьях по этому сервису: «Nginx: установка и первый сайт», «Apache: установка и виртуальные хосты», «PostgreSQL: установка и настройка», «MySQL / MariaDB: установка и настройка», «Redis: установка и настройка».