Node.js на VPS: деплой приложения с PM2 и Nginx

Боты, ИИ и автоматизация
Node.js на VPS: деплой приложения с PM2 и Nginx

Запустить Node.js на VPS — задача на 30–40 минут, если делать всё в правильном порядке. В этом руководстве разберём деплой Node.js-приложения на сервер с Ubuntu 24.04 LTS от и до: установка Node.js через NodeSource или nvm, запуск процесса под управлением PM2 с автостартом после перезагрузки, настройка Nginx как reverse proxy и подключение HTTPS. Все команды рабочие — можно копировать в терминал. На выходе получите продакшен-схему, которой пользуются и одиночные разработчики, и небольшие команды: Node-процесс слушает локальный порт, Nginx принимает трафик из интернета, PM2 следит, чтобы приложение не падало.

Что понадобится

  • VPS с Ubuntu 24.04 LTS и root-доступом. Для API или SSR-приложения на Next.js/Nuxt хватит тарифа Start у Cheap-Host — 450 ₽/мес за 6 vCPU, 8 ГБ RAM и 96 ГБ NVMe. NVMe заметно ускоряет npm ci и сборку фронтенда.
  • Домен, направленный на IP сервера (для HTTPS — обязательно).
  • SSH-доступ — если подключаетесь впервые, смотрите инструкцию как подключиться к VPS по SSH.
  • Приложение в git-репозитории — в примерах используем условный API на Express, слушающий порт 3000.

Шаг 1. Подготовка сервера

Обновите пакеты и откройте нужные порты. Наружу должны смотреть только SSH, HTTP и HTTPS — порт самого приложения (3000) закрыт файрволом:

sudo apt update && sudo apt upgrade -y
sudo apt install -y git curl ufw
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Подробнее о правилах файрвола — в статье про настройку UFW на Ubuntu.

Шаг 2. Установка Node.js: NodeSource или nvm

В репозиториях Ubuntu версия Node.js обычно отстаёт, поэтому используют один из двух способов.

Вариант А: NodeSource (рекомендуется для продакшена)

Node.js ставится системно, обновляется вместе с apt upgrade, одинаково доступен всем пользователям и systemd-службам:

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

Вариант Б: nvm (несколько версий Node.js на одном сервере)

Менеджер версий nvm ставит Node.js в домашний каталог пользователя. Удобно, когда на сервере живут проекты с разными версиями:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
source ~/.bashrc
nvm install --lts
node -v

Важно: при nvm-установке PM2 и Node.js принадлежат конкретному пользователю — команду автозапуска pm2 startup нужно выполнять от него же, а не от root.

Шаг 3. Деплой кода и зависимости

Разместим приложение в /var/www:

sudo mkdir -p /var/www && sudo chown $USER:$USER /var/www
cd /var/www
git clone https://github.com/youruser/yourapp.git app
cd app
npm ci --omit=dev
cp .env.example .env
nano .env

npm ci вместо npm install ставит зависимости строго по package-lock.json — сборка воспроизводима, а на NVMe-диске это занимает секунды. Секреты (порт, строка подключения к базе, API-ключи) держите в .env, а не в коде. Если проект собирается (TypeScript, Next.js), выполните npm run build.

Шаг 4. PM2: запуск, логи и автозапуск

PM2 — менеджер процессов для Node.js: перезапускает упавшее приложение, пишет логи, умеет cluster-режим и нулевой простой при обновлении.

sudo npm install -g pm2
cd /var/www/app
pm2 start app.js --name myapp
pm2 status

Для приложения с npm-скриптом запуска: pm2 start npm --name myapp -- start. Если API упирается в CPU, включите cluster-режим — по процессу на каждое ядро:

pm2 start app.js --name myapp -i max

Теперь автозапуск после перезагрузки сервера — два обязательных шага, которые чаще всего забывают:

pm2 startup systemd
# выполните команду, которую выведет pm2 startup
pm2 save

pm2 startup создаёт systemd-юнит, а pm2 save фиксирует текущий список процессов — без него после ребута PM2 стартует пустым. Полезные команды на каждый день:

  • pm2 logs myapp — логи в реальном времени;
  • pm2 monit — CPU и память по процессам;
  • pm2 reload myapp — перезапуск без простоя (в cluster-режиме);
  • pm2 restart myapp — обычный перезапуск.

Шаг 5. Nginx как reverse proxy

Nginx принимает запросы на 80/443 и проксирует их в Node-процесс на 127.0.0.1:3000. Установка и конфиг:

sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/myapp

Содержимое файла:

server {
    listen 80;
    server_name example.com www.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;
    }
}

Заголовки Upgrade и Connection нужны для WebSocket (Socket.IO и т.п.). Активируйте конфиг и проверьте синтаксис:

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

Откройте http://ваш-IP — должно отвечать приложение. У Cheap-Host каждый VPS получает выделенный IPv4, так что проверить можно ещё до привязки домена.

Шаг 6. Домен и HTTPS

Создайте A-запись домена, указывающую на IP сервера (пошагово — в статье как привязать домен к VPS), затем выпустите бесплатный сертификат:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

Certbot сам поправит конфиг Nginx и настроит автопродление. Детали и проверка автопродления — в руководстве по Let's Encrypt.

Типичные ошибки при деплое Node.js

  • Приложение слушает 0.0.0.0 с открытым портом. Node-процесс должен слушать 127.0.0.1, а наружу смотрит только Nginx.
  • Забыли pm2 save. После перезагрузки сервера приложение не поднялось — классика. pm2 startup + pm2 save всегда идут парой.
  • 502 Bad Gateway. Nginx жив, а Node-процесс — нет. Смотрите pm2 logs: чаще всего не хватает переменной в .env или порт в конфиге не совпадает с портом приложения.
  • Сборка падает по памяти. На серверах с 1–2 ГБ RAM npm run build может убиваться OOM-киллером. Помогает swap или тариф с запасом RAM — у Start от Cheap-Host 8 ГБ, чего хватает и на сборку Next.js.
  • Деплой под root. Заведите отдельного пользователя с sudo и запускайте PM2 от него.

FAQ

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

Технически да, но не стоит. Nginx отдаёт статику быстрее Node.js, терминирует TLS, ограничивает размер запросов и позволяет разместить несколько приложений на одном сервере. Node-процесс при этом слушает только 127.0.0.1 и не торчит в интернет.

Что выбрать для продакшена: NodeSource или nvm?

Для сервера с одним приложением удобнее NodeSource: Node.js ставится системно и обновляется через apt. nvm полезен, когда на сервере несколько проектов с разными версиями Node.js.

Сколько ресурсов нужно Node.js-приложению на VPS?

Типичный API на Express/Fastify занимает 80–200 МБ RAM на процесс. Пет-проекту хватит 1–2 ГБ, для продакшена с cluster-режимом считайте по процессу на ядро плюс запас на сборки. Тариф Start (6 vCPU, 8 ГБ RAM) закрывает большинство сценариев.

Когда нужен cluster-режим PM2?

Node.js однопоточный: один процесс грузит одно ядро. Если приложение упирается в CPU, запустите pm2 start app.js -i max — PM2 поднимет по процессу на ядро и распределит запросы между ними без изменения кода.

Как обновлять приложение без простоя?

git pull, npm ci, при необходимости npm run build, затем pm2 reload myapp вместо restart — в cluster-режиме процессы перезапустятся по очереди, и сервис продолжит отвечать.

Вывод

Схема «Node.js + PM2 + Nginx» — проверенный минимум для продакшена: PM2 держит процесс живым и стартует его после ребута, Nginx закрывает TLS, статику и WebSocket, а приложение остаётся за файрволом. Освоив её, логичный следующий шаг — упаковать сервис в контейнер: смотрите Docker на VPS. Если для деплоя нужен сервер — у Cheap-Host тариф Start от 450 ₽/мес: 6 vCPU, 8 ГБ RAM, NVMe, KVM с полным root, безлимитный трафик и выдача сервера за 45–60 секунд. Выбрать конфигурацию можно на cheap-host.onl.

Читайте также