OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

Node.js на VDS в проде: PM2, systemd, Nginx reverse proxy, логи и автозапуск

Разбираем боевую схему прод-развертывания Node.js на VDS: когда выбирать PM2 или чистый systemd, как настроить Nginx как reverse proxy с TLS и WebSocket, обеспечить автозапуск и graceful reload, ротацию логов, health-check и защитные лимиты. В конце — чек-лист и примеры конфигов.
Node.js на VDS в проде: PM2, systemd, Nginx reverse proxy, логи и автозапуск

Node.js в продакшене на VDS — это не только про «запустить сервер на порту 3000». Чтобы приложение переживало рестарты, умело выкатываться без простоя, получало трафик через TLS и адекватно логировалось, нужна минимальная, но системная обвязка. В этой статье разбираем практическую схему: Nginx как reverse proxy, PM2 или systemd для управления процессами, корректный логинг и автозапуск.

Архитектурный обзор: роли Nginx, Node.js, PM2 и systemd

Классическая прод-схема строится по принципу разделения обязанностей:

  • Nginx принимает внешние HTTP(S)-запросы, завершает TLS, распределяет трафик и проксирует на локальный Node.js.
  • Node.js отвечает за бизнес-логику и отдачу API/рендеринга.
  • PM2 или systemd управляет жизненным циклом процессов: автозапуск, перезапуск при сбоях, обновления без простоя.

В проде Node.js не должен слушать публичный интерфейс. Прячьте приложение за Nginx и слушайте только 127.0.0.1. Это закрывает доступ напрямую, упрощает фаерволл и TLS.

PM2 или systemd: когда что выбрать

Обе опции жизнеспособны, но закрывают разные кейсы.

PM2: просто, быстро, многофункционально

Подходит, если хочется минимум системной возни и максимум удобства разработчика: кластерный режим по CPU, graceful reload, встроенная ротация логов через модуль, мониторинг и ресуррект процессов после перезагрузки. PM2 сам интегрируется с systemd (командой pm2 startup), так что автозапуск после ребута системы решается в один шаг.

Чистый systemd: предсказуемо и нативно

Сильная сторона — нативная интеграция с ОС, прозрачные права и политики, journald для логов, единое место управления сервисами. Хороший вариант для минималистичных окружений, контейнеров и команд, где уже стандартизован systemd. Мягкие рестарты зависят от реализации в приложении (сигналы, socket activation), зато меньше «магии» вокруг.

Практика: для большинства небольших и средних Node.js-проектов начинается с PM2. Когда пайплайн и мониторинг стабилизируются, часть команд мигрирует на чистый systemd ради унификации.

Конфигурация Nginx для reverse proxy и WebSocket к Node.js

Подготовка окружения и базовое приложение

Установите LTS-версию Node.js из родного репозитория дистрибутива или официальных сборок. Создайте пользователя без прав root для приложения, выделите рабочую директорию, не храните секреты в репозитории.

# пример: создание пользователя и директории
sudo useradd -r -m -d /opt/myapp -s /usr/sbin/nologin myapp
sudo mkdir -p /opt/myapp
sudo chown -R myapp:myapp /opt/myapp

Минимальный HTTP-сервер для проверки:

// file: /opt/myapp/server.js
const http = require('http');
const port = process.env.PORT || 3000;
const server = http.createServer((req, res) => {
  if (req.url === '/health') {
    res.writeHead(200, {'Content-Type': 'application/json'});
    return res.end(JSON.stringify({status: 'ok'}));
  }
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello from Node.js on VDS\n');
});
server.listen(port, '127.0.0.1', () => {
  console.log(`listening on 127.0.0.1:${port}`);
});
FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Запуск через PM2: кластер, автозапуск, ротация логов

Установите PM2 глобально и создайте конфиг окружения.

sudo npm i -g pm2

Конфигурация ecosystem.config.js с кластерным режимом по числу CPU:

// file: /opt/myapp/ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'myapp',
      script: './server.js',
      cwd: '/opt/myapp',
      instances: 'max',
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'production',
        PORT: 3000
      },
      max_memory_restart: '512M',
      out_file: '/var/log/myapp/app.log',
      error_file: '/var/log/myapp/error.log',
      merge_logs: false,
      time: true
    }
  ]
};
sudo mkdir -p /var/log/myapp
sudo chown -R myapp:myapp /var/log/myapp

Старт, сохранение состояния и автозапуск:

sudo -u myapp pm2 start /opt/myapp/ecosystem.config.js
sudo -u myapp pm2 status
sudo -u myapp pm2 save
sudo pm2 startup systemd -u myapp --hp /opt/myapp

Резервирование «оживления» после ребута системой выполнит связка pm2 save + pm2 startup. Для обновления кода без даунтайма используйте pm2 reload myapp.

Ротация логов через модуль:

sudo -u myapp pm2 install pm2-logrotate
sudo -u myapp pm2 set pm2-logrotate:max_size 50M
sudo -u myapp pm2 set pm2-logrotate:retain 14
sudo -u myapp pm2 set pm2-logrotate:compress true
sudo -u myapp pm2 set pm2-logrotate:dateFormat YYYY-MM-DD
sudo -u myapp pm2 save

Если требуется централизованный сбор логов, направляйте их в stdout/stderr и забирайте через journald или агент. PM2 позволяет отключить файлы и писать в консоль.

Запуск через systemd: юнит, переменные окружения, перезапуски

Создайте файл окружения с переменными и ограничьте права доступа:

sudo tee /etc/myapp.env > /dev/null << 'EOF'
NODE_ENV=production
PORT=3000
EOF
sudo chown root:root /etc/myapp.env
sudo chmod 600 /etc/myapp.env

Юнит-файл сервиса:

# file: /etc/systemd/system/myapp.service
[Unit]
Description=My Node.js app
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp.env
ExecStart=/usr/bin/node /opt/myapp/server.js
Restart=always
RestartSec=2
# полезные лимиты
LimitNOFILE=65536
# логирование в journald
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target

Активация и проверка:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myapp
journalctl -u myapp -f

Для бесшовных релизов реализуйте graceful shutdown: ловите SIGTERM, перестаньте принимать новые соединения, дождитесь активных запросов и завершите процесс. Тогда systemctl restart myapp пройдет мягко.

Юнит systemd и настройка PM2 для Node.js сервиса

Nginx как reverse proxy с SSL и WebSocket

Nginx принимает публичный трафик и шифрует его. Node.js слушает только локальный интерфейс. Покажем две секции: редирект HTTP на HTTPS и основную HTTPS-конфигурацию. Если у вас ещё нет сертификата — оформите SSL-сертификаты, а домен можно подключить через нашу регистрация доменов.

# file: /etc/nginx/sites-available/myapp.conf
server {
  listen 80;
  listen [::]:80;
  server_name example.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name example.com;

  ssl_certificate /etc/ssl/myapp/fullchain.pem;
  ssl_certificate_key /etc/ssl/myapp/privkey.pem;
  # базовые настройки TLS, шифры, протоколы и OCSP stapling задайте в общем include

  # защита от слишком больших загрузок
  client_max_body_size 20m;

  # лог формат по желанию: можно использовать json
  access_log /var/log/nginx/myapp.access.log;
  error_log /var/log/nginx/myapp.error.log warn;

  # прокси до локального Node.js
  location / {
    proxy_pass http://127.0.0.1:3000;

    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # WebSocket
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_read_timeout 60s;
    proxy_send_timeout 60s;
    proxy_connect_timeout 5s;

    # при долгих SSE можно отключить буферизацию
    # proxy_buffering off;
  }

  location = /health {
    access_log off;
    proxy_pass http://127.0.0.1:3000/health;
  }
}

Активируйте конфиг и перезапустите Nginx:

sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/myapp.conf
sudo nginx -t
sudo systemctl reload nginx

Для HSTS включайте заголовок только после уверенного запуска HTTPS, чтобы не запереть клиентов на нерабочем домене. Подробно о редиректах и HSTS мы писали в заметке: миграция с 301 и HSTS.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Логирование: Nginx, Node.js, PM2/systemd

Комбинируйте уровни логирования: баланс удобства и объема.

  • Nginx: access/error в отдельные файлы, краткая ротация.
  • Node.js: структурированные JSON-логи в stdout/stderr, чтобы их подхватывал journald или PM2.
  • PM2 или systemd: ответственность за хранение/ротацию и просмотр.

Пример расширенной ротации для Nginx:

# file: /etc/logrotate.d/nginx-myapp
/var/log/nginx/myapp.access.log /var/log/nginx/myapp.error.log {
  daily
  rotate 14
  missingok
  compress
  delaycompress
  notifempty
  sharedscripts
  postrotate
    systemctl reload nginx >/dev/null 2>&1 || true
  endscript
}

Если используете PM2-файлы логов, модуль pm2-logrotate уже покрывает задачи ротации. Альтернатива — писать только в stdout/stderr:

// фрагмент ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'myapp',
      script: './server.js',
      out_file: '/dev/stdout',
      error_file: '/dev/stderr',
      time: true
    }
  ]
};

Структурные логи в приложении:

// пример простого JSON-логирования
function log(level, msg, extra) {
  const rec = Object.assign({
    ts: new Date().toISOString(),
    level,
    msg
  }, extra || {});
  console.log(JSON.stringify(rec));
}

log('info', 'server start', {port: process.env.PORT});

Автозапуск и устойчивость к сбоям

С PM2:

  • pm2 save сохраняет список процессов.
  • pm2 startup генерирует юнит и включает автозапуск для пользователя.
  • pm2 resurrect поднимает процессы после перезагрузки вручную, если нужно.

С systemd:

  • systemctl enable myapp для автозапуска при старте ОС.
  • Restart=always и RestartSec в юните для автоматического рестарта при ошибке.
  • StartLimitIntervalSec и StartLimitBurst контролируют флэппинг.

Следите за лимитами дескрипторов (LimitNOFILE) и используйте health-check для внешнего мониторинга. Рестарты без потерь требуют корректной обработки сигналов в приложении.

WebSocket и sticky-сессии: что важно знать

Если приложение держит WebSocket-соединения, проверьте:

  • В Nginx проставлены Upgrade/Connection заголовки (см. конфиг выше).
  • Таймауты proxy_read_timeout увеличены, если соединения долгие.
  • PM2 в cluster-режиме принимает один порт и сам делит соединения между воркерами, что обычно достаточно для WS.

Если вы запускаете несколько независимых экземпляров приложения на разных портах и балансируете их в Nginx через upstream, для sticky-сессий можно использовать ip_hash или проксировать маркер сессии в бэкенд и реализовать привязку самостоятельно.

Обновления без даунтайма

С PM2 все просто:

  • pm2 reload myapp — graceful перезагрузка воркеров по одному.
  • pm2 deploy — можно собрать простой деплой-хук без простоя.

С systemd используйте свой механизм graceful shutdown в приложении и стратегию смены версии:

  • Запустите новую версию на другом порту (myapp@3001),
  • переключите прокси на новый порт (nginx reload),
  • корректно завершите старую версию.

Даже если у вас stateless API, планируйте миграции БД с учетом совместимости схемы и двустороннего отката. Про перенос проектов без простоя читайте также: миграция без даунтайма.

Безопасность и эксплуатационные мелочи

  • Запускайте приложение от отдельного пользователя без shell-доступа.
  • Слушайте только 127.0.0.1; внешне открыты лишь 80/443.
  • Секреты храните в EnvironmentFile с правами 600; не коммитьте их в репозиторий.
  • Ограничьте client_max_body_size и проверьте обработку загрузок.
  • Добавьте /health или /ready endpoint, чтобы отделить проверку готовности от факта «живости» процесса.
  • Резервное копирование конфигов Nginx, systemd-юнитов и env-файлов — часть базового бэкапа.

Траблшутинг: типичные грабли

  • 502 Bad Gateway: проверьте, слушает ли Node.js 127.0.0.1:PORT, совпадает ли порт в Nginx, нет ли SELinux/AppArmor блокировок.
  • WebSocket не коннектится: заголовки Upgrade/Connection, HTTP/1.1 к апстриму, таймауты.
  • Слетает после ребута: для PM2 выполните pm2 save и pm2 startup; для systemd — systemctl enable.
  • Логи растут без меры: включите pm2-logrotate или logrotate для Nginx, настройте ретеншен.
  • Нет IP клиента в приложении: используйте X-Forwarded-For и читайте его в приложении.

Чек-лист продовой сборки

  • Nginx слушает 80/443, редиректит на HTTPS, проксирует на 127.0.0.1:PORT.
  • Сертификаты валидны и продлеваются автоматически, HSTS подключается осознанно.
  • Node.js стартует через PM2 или systemd, перезапускается автоматически.
  • Логи ротируются, хранение и формат согласованы.
  • Health-check отдает 200 быстро и стабильно.
  • Таймауты и лимиты настроены, апстрим держит WebSocket/SSE.
  • Релизы проходят без простоя по отработанной процедуре.

Итог: минимальная, но правильная обвязка — это Nginx как reverse proxy с TLS, PM2 или systemd для управления процессами, четкая стратегия логирования и автозапуска. Такая схема укладывается в несколько конфигов, легко поддерживается и масштабируется по мере роста нагрузки.

Поделиться статьей

Вам будет интересно

CDC в PostgreSQL с Debezium: logical decoding, Kafka/Redpanda и outbox OpenAI Статья написана AI (GPT 5)

CDC в PostgreSQL с Debezium: logical decoding, Kafka/Redpanda и outbox

Разбираем, как включить logical decoding, настроить replication slots, выбрать pgoutput или wal2json и подключить Debezium к Kafka ...
cert-manager в Kubernetes: Issuer, ClusterIssuer и DNS-01 на практике OpenAI Статья написана AI (GPT 5)

cert-manager в Kubernetes: Issuer, ClusterIssuer и DNS-01 на практике

Подробный разбор автоматизации TLS в Kubernetes с cert-manager: когда выбирать Issuer или ClusterIssuer, как настроить ACME DNS-01 ...
Redis replication практикум: PSYNC2, diskless, failover и измеримый RPO OpenAI Статья написана AI (GPT 5)

Redis replication практикум: PSYNC2, diskless, failover и измеримый RPO

Разбираем практический план настройки и измерений: как работает PSYNC2 и репликационный backlog, чем полезна бездисковая репликаци ...