Node.js приложение с PM2 и Nginx
Node.js-приложение, запущенное просто командой node app.js в SSH-сессии, умирает вместе с этой сессией и не переживёт падение или перезагрузку сервера. В этой статье вы установите Node.js, запустите приложение через менеджер процессов PM2 (автоперезапуск при падении, автозапуск после reboot), поставите перед ним Nginx как реверс-прокси и проверите, что всё это переживёт перезагрузку VPS.
Что понадобится
- VPS ForestsNet Cloud на Ubuntu 22.04/24.04, Debian 11/12 или AlmaLinux/Rocky Linux 8/9.
- Доступ по SSH под
rootлибо пользователем с правамиsudo. - Код вашего Node.js-приложения уже загружен на сервер (например, через
git cloneилиscp) и в нём естьpackage.json. В примерах ниже используется путь/var/www/myapp— замените на свой. - (опционально) домен, A-запись которого указывает на IP сервера — понадобится для
server_nameв Nginx. Без домена тоже можно, тогда используйте IP-адрес сервера.
ℹ️ Совет: команды ниже даны с sudo. Если вы уже подключены как root и sudo в системе нет — выполняйте те же команды без префикса sudo.
Шаг 1. Установите Node.js
Два стандартных способа поставить Node.js на VPS: системный пакет из репозитория NodeSource (рекомендуется для продакшена — сразу интегрируется с systemd и PM2) или nvm (удобен, если нужны несколько версий Node или установка без root).
⚠️ Важно: не ставьте Node.js командой apt install nodejs / dnf install nodejs без добавления репозитория — в штатных репозиториях дистрибутивов версия Node.js обычно сильно устаревшая.
Вариант А: через NodeSource (системный Node.js)
Актуальная LTS-ветка на момент написания — Node.js 24.x. Подставьте нужную версию вместо 24, если требуется другая (например, 22 — предыдущая LTS-ветка).
Ubuntu/Debian:
curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt-get install -y nodejsAlmaLinux/Rocky Linux:
sudo dnf module disable -y nodejs
curl -fsSL https://rpm.nodesource.com/setup_24.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo dnf install -y nodejsℹ️ Совет: на AlmaLinux/Rocky по умолчанию включён встроенный модуль nodejs из AppStream — dnf module disable -y nodejs отключает его, чтобы пакет не конфликтовал с версией из NodeSource.
Проверьте установку:
node -v
npm -vℹ️ Совет: вместо конкретного номера версии можно использовать setup_lts.x — он всегда указывает на текущую LTS-ветку Node.js.
Вариант Б: через nvm (без root, несколько версий Node)
nvm ставит Node.js в домашнюю директорию пользователя — без root и с возможностью держать несколько версий и переключаться между ними.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bashℹ️ Совет: на момент написания статьи актуальный тег install-скрипта — v0.40.5; свежий номер смотрите на странице релизов nvm-sh/nvm на GitHub.
Переоткройте SSH-сессию или выполните для текущего шелла (bash):
source ~/.bashrcУстановите и включите текущую LTS-версию:
nvm install --lts
nvm use --lts
node -v⚠️ Важно: nvm ставит Node.js только для текущего пользователя. Если планируете запускать PM2 как systemd-сервис (шаг 3), выполняйте pm2 startup под этим же пользователем — команда сама подставит правильный путь до node; скопируйте её точно как есть, не переписывая вручную.
Шаг 2. Установите PM2 и запустите приложение
ℹ️ Совет: в продакшене приложение часто запускают не под root, а под отдельным непривилегированным пользователем — тогда sudo нужен только для системных шагов (Node.js, Nginx, файрвол), а pm2/npm выполняются от его имени.
- Установите PM2 глобально:
sudo npm install -g pm2ℹ️ Если Node.js установлен через nvm, sudo не нужен: npm install -g pm2.
- Перейдите в директорию приложения и установите зависимости:
cd /var/www/myapp
npm ciℹ️ Если package-lock.json в проекте нет — используйте npm install.
- Запустите приложение через PM2:
pm2 start app.js --name myapp⚠️ Важно: приложение должно слушать только 127.0.0.1 (localhost), а не 0.0.0.0 — наружу его будет публиковать Nginx (шаг 4). Например, в Express: app.listen(3000, '127.0.0.1').
- Проверьте статус и логи:
pm2 status
pm2 logs myappℹ️ Совет: для повторяемого запуска (особенно если приложений несколько) удобнее описать процесс в ecosystem.config.js:
module.exports = {
apps: [
{
name: "myapp",
script: "./app.js",
cwd: "/var/www/myapp",
instances: 1,
exec_mode: "fork",
env: {
NODE_ENV: "production",
PORT: 3000
}
}
]
};Запуск: pm2 start ecosystem.config.js.
Шаг 3. Сохраните список процессов и включите автозапуск PM2
Пока список процессов PM2 не сохранён и автозапуск не настроен, приложение не переживёт перезагрузку сервера.
- Сгенерируйте startup-скрипт:
pm2 startupPM2 определит используемую init-систему (systemd) и выведет готовую команду примерно такого вида:
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u root --hp /rootСкопируйте и выполните именно ту команду, которую PM2 вывел у вас, — путь PATH, пользователь (-u) и домашняя директория (--hp) будут подставлены под вашу систему.
- Сохраните текущий список процессов:
pm2 save✅ Готово: теперь после перезагрузки сервера systemd поднимет PM2, а PM2 — все сохранённые процессы.
ℹ️ Совет: после любого изменения списка процессов (добавили или убрали приложение) заново выполняйте pm2 save. После обновления версии Node.js выполните pm2 unstartup, затем заново pm2 startup — иначе systemd-сервис будет ссылаться на старый бинарник node.
Шаг 4. Настройте Nginx как реверс-прокси
- Установите Nginx и включите автозапуск:
Ubuntu/Debian:
sudo apt-get install -y nginx
sudo systemctl enable --now nginxAlmaLinux/Rocky Linux:
sudo dnf install -y nginx
sudo systemctl enable --now nginx- Создайте конфиг для сайта — путь зависит от дистрибутива.
Debian/Ubuntu: sites-available/sites-enabled
sudo nano /etc/nginx/sites-available/myappAlmaLinux/Rocky Linux: conf.d
sudo nano /etc/nginx/conf.d/myapp.confСодержимое файла одинаковое для обоих вариантов:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
}Замените example.com на свой домен или IP сервера, 127.0.0.1:3000 — на адрес и порт, которые слушает приложение.
Для Debian/Ubuntu дополнительно включите сайт симлинком:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/Проверьте конфигурацию и перечитайте её:
sudo nginx -t
sudo systemctl reload nginx⚠️ Важно: на AlmaLinux/Rocky Linux с включённым SELinux (по умолчанию — enforcing) Nginx по умолчанию не может проксировать запросы даже на локальный TCP-порт — вы получите 502 Bad Gateway. Разрешите это один раз:
sudo setsebool -P httpd_can_network_connect onℹ️ Совет: строки proxy_http_version 1.1, Upgrade и Connection "upgrade" в конфиге выше нужны, если приложение использует WebSocket (например, Socket.IO или HMR у Next.js/Vite). Если это не ваш случай, их можно убрать.
Шаг 5. Откройте порты в файрволе
Порт 80 (и 443, если позже добавите HTTPS) должен быть открыт наружу. Порт самого приложения (в примере — 3000) открывать во внешнем файрволе не нужно — к нему обращается только Nginx на этом же сервере.
Ubuntu/Debian (ufw):
sudo ufw allow 80,443/tcpПодробнее про настройку — в статье «Файрвол ufw».
AlmaLinux/Rocky Linux (firewalld):
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload⚠️ Важно: не открывайте порт приложения (в примере — 3000) во внешнем файрволе — весь трафик снаружи должен идти только через Nginx на 80/443.
Шаг 6. Проверьте, что всё переживёт перезагрузку
- Убедитесь, что и Nginx, и PM2 включены в автозапуск:
systemctl is-enabled nginx
systemctl is-enabled pm2-rootОбе команды должны вывести enabled (замените root на своего пользователя, если pm2 startup выполняли не под root).
- Проверьте на деле — перезагрузите сервер:
sudo rebootПодождите минуту-две, подключитесь по SSH заново и проверьте:
pm2 status
curl -I http://127.0.0.1:3000
curl -I http://localhost/
systemctl status nginxЕсли pm2 status показывает приложение в статусе online, а оба curl возвращают HTTP-ответ — всё настроено верно.
✅ Готово: приложение доступно через Nginx по вашему домену или IP и переживёт любую перезагрузку сервера.
Что дальше
Дальше обычно добавляют HTTPS (например, через Let's Encrypt/certbot) и открывают правило для 443 в файрволе (см. шаг 5). Если на сервере несколько приложений — заведите под каждое отдельный конфиг Nginx и отдельную запись в ecosystem.config.js, и не забывайте про pm2 save после любых изменений списка процессов.