VDS · Сайты и веб-серверы

Приложение на Node.js под systemd

Как запустить Node-приложение на VDS так, чтобы оно пережило разрыв SSH и перезагрузку машины: unit systemd, переменные окружения, логи.

Обновлено 23 августа 2026 г.

Запущенный вручную node index.js умирает вместе с сессией SSH. Чтобы приложение работало постоянно и поднималось после перезагрузки, его оформляют службой systemd — это встроенный механизм системы, ничего доставлять не нужно.

Подготовка

Поставьте Node из официального репозитория, а не из старого системного пакета:

curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt install -y nodejs
node -v

Заведите отдельного пользователя — приложение не должно работать от root:

useradd -r -m -d /srv/app -s /usr/sbin/nologin appuser
chown -R appuser:appuser /srv/app
sudo -u appuser npm ci --omit=dev --prefix /srv/app

Служба systemd

Файл /etc/systemd/system/myapp.service:

[Unit]
Description=My Node app
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/srv/app
EnvironmentFile=/srv/app/.env
ExecStart=/usr/bin/node /srv/app/index.js
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true

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

Restart=always поднимет процесс после падения. EnvironmentFile — простой файл KEY=value без кавычек и без export; права на него поставьте 600, там лежат пароли.

Наружу через прокси

Приложение должно слушать только 127.0.0.1:3000, а наружу его отдаёт веб-сервер с сертификатом:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Подробнее — обратный прокси и Let's Encrypt. Если приложение слушает 0.0.0.0, оно доступно из интернета в обход прокси и без шифрования.

Логи и обновление

journalctl -u myapp -f
journalctl -u myapp --since "1 hour ago" -p err
systemctl restart myapp

Вывод в stdout попадает в journal автоматически — свои файлы логов и их ротацию заводить не нужно.

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

СимптомПричина
status=203/EXECНеверный путь к node или к файлу в ExecStart — пишите абсолютные пути
Служба перезапускается по кругуПриложение падает на старте: смотрите journalctl -u myapp -n 50
EADDRINUSEОстался запущенный вручную процесс: ss -tulpn | grep 3000
Работает в SSH, но не как службаНе хватает переменных окружения — их нет в systemd, задайте в EnvironmentFile
Сборка падает по памяти на VDS-1Собирайте локально, на машину заливайте готовое

Не ставьте pm2 поверх systemd. Два супервизора над одним процессом дают путаницу: systemd считает процесс живым, а pm2 в это время его перезапускает.