Вы купили VPS у российского хостера, у вас есть Node.js-проект - API, бот, веб-приложение - и стоит задача поднять его в продакшн. Задача стандартная, путь изведанный, но именно на нём спотыкаются чаще всего: то порт не открыт в firewall, то сервер после ребута молчит, то приложение слушает не тот адрес.

Коротко, что вам понадобится: VPS на 1-2 ГБ RAM с NVMe и Ubuntu или Debian, SSH-ключ, Node.js LTS (Node 26 - Active LTS с 28 октября 2026), процесс-менеджер PM2 для автозапуска, Nginx спереди как reverse proxy и бесплатный SSL через Certbot. Весь путь - примерно 20-30 минут ручной работы, дальше обновления сводятся к паре команд.

Честная оговорка про производительность. Мы не арендовали инстансы у российских хостеров для этой статьи, поэтому не гоняли бенчмарки и не приводим замеренных цифр (req/s, времени отклика и так далее). Ниже - только проверенные команды и официальные характеристики; о скорости говорим как о заявленной, а не как о результате нашего теста.

1. Какой VPS взять у российских хостеров

Для Node.js-приложения решающие ресурсы - память и диск, а не количество ядер. Практическое правило для бота, API или веб-приложения на Express/Fastify: 1-2 ГБ RAM достаточно для продакшена, а для сборки (npm ci, next build) комфортнее 2 ГБ, потому что сборка ест память и при нехватке сервер уходит в своп. На входных тарифах почти все российские провайдеры уже ставят NVMe - это важно и для установки пакетов, и для холодного старта.

Второй частый сюрприз - публичный IPv4. Для веб-сервера, бота, принимающего webhook, или API он обязателен, но не всегда включён в цену. У Timeweb публичный IPv4 тарифицируется отдельно - 150 рублей в месяц за адрес, один IPv6 бесплатно (переключатель «Публичный IP» в карточке включён по умолчанию).[1] У Beget публичный IPv4 оплачивается отдельно - 5 рублей в день в дополнение к конфигурации.[2] Есть хостеры, где адрес уже в цене, так что перед покупкой проверяйте итоговую сумму с учётом IP - у кого-то это добавляет 100-150 рублей к месячному счёту.

Для ориентира, актуальные входные тарифы на момент написания (полное сравнение тарифов и где публичный IPv4 уже в цене - в нашем обзоре лучший VPS для Node.js в России):

  • Timeweb Cloud: Cloud-40 (2 x 3.3 ГГц / 2 ГБ / 40 ГБ NVMe) - 2800 руб/мес, следующая ступень Cloud-50 (2 x 3.3 ГГц / 4 ГБ / 50 ГБ) - 3840 руб/мес (прайс на 24.10.2026). Биллинг почасовой, образы Ubuntu, Debian и другие ставятся сразу.[1]
  • Beget: 2 ядра / 2 ГБ / 30 ГБ NVMe - 38,44 руб/день, плюс публичный IPv4 5 руб/день (итого 43,44 руб/день). В маркетплейсе при создании сервера доступны Ubuntu 26.04, Ubuntu 24.04, Docker и ещё десятки приложений.[2]

Дистрибутив берите Ubuntu или Debian - это то, под что проще всего находятся инструкции и пакеты, включая NodeSource и Certbot. Подойдёт любой актуальный LTS: на момент написания свежий Ubuntu LTS - 26.04.[11]

2. Базовый апгрейд и SSH по ключу

После создания сервера вы получите IP и root-доступ. Первое, что надо сделать, - зайти по SSH и закрыть дверь: только вход по ключу, без root-логина и пароля.

Сгенерируйте ключ на своей машине (один раз) и загрузите публичную часть на сервер:

# на вашей машине
ssh-keygen -t ed25519
ssh-copy-id root@<ip-сервера>

Подключитесь и обновите систему:

ssh root@<ip-сервера>
apt update && apt upgrade -y

Создайте рабочего пользователя и настройте firewall. Порты, которые нужны веб-серверу: 22 (SSH), 80 (HTTP), 443 (HTTPS):

adduser deploy
usermod -aG sudo deploy

# переносим ключ рабочему пользователю
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh && chmod 600 /home/deploy/.ssh/authorized_keys

# firewall
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable

Затем отключите root-логин и вход по паролю в /etc/ssh/sshd_config:

PermitRootLogin no
PasswordAuthentication no

Перезапустите SSH (systemctl restart ssh) и не закрывайте текущую сессию, пока не проверите, что новый вход под deploy по ключу работает. Это классическая точка невозврата: кто запер себя, тот стучится в консоль хостера.

На маленьких инстансах (1 ГБ и меньше) полезно сразу включить swap, чтобы сервер не убивал приложение при нехватке памяти. Команды для swap-файла:

fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab

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

Есть два основных рабочих способа поставить Node на чистый сервер. Оба официальные, выбор - дело вкуса и задач.

Вариант A: nvm (менеджер версий). Удобен, если вы хотите быстро переключаться между версиями Node или держать несколько проектов на разных версиях. Официальный способ установки nvm - через install.sh, текущая версия на момент написания v0.40.8:[4]

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash
# перезагрузите шелл или: source ~/.bashrc
nvm install 26      # ставит и переключает на Node 26 (Active LTS с 28.10.2026)
node -v             # проверка

Вариант B: NodeSource-репозиторий (системный пакет). Ставит Node как системный пакет через apt, что удобно на одном сервере без переключения версий. Официальные скрипты NodeSource добавляют официальный deb-репозиторий:[5]

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

Какую версию брать. Для продакшена - только LTS, и на 30 октября 2026 это Node 26 (Active LTS с 28 октября 2026, актуальный релиз ветки v26.13.0). Node 24 перешёл в Maintenance LTS 20 октября 2026 и поддерживается до 30 апреля 2028, но новым деплоям незачем начинать с него.[3] Версию имеет смысл зафиксировать в package.json, чтобы деплой был воспроизводимым:

"engines": { "node": ">=24" }

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

4. Загрузка кода на сервер: git clone или rsync

Зависит от того, как устроен ваш проект.

git clone - основной способ для проекта в репозитории. Приложению нужна собственная директория. Если репозиторий приватный, добавьте на сервер deploy-ключ (отдельную SSH-пару) в настройки репозитория, чтобы не светить личный ключ:

sudo mkdir -p /home/deploy/apps
sudo chown deploy:deploy /home/deploy/apps
git clone git@github.com:you/myapp.git /home/deploy/apps/myapp
cd /home/deploy/apps/myapp

rsync/scp - когда нужно дотащить локальную сборку или нет git. Полезно для первого деплоя, когда репозиторий ещё не на месте. rsync удобнее scp при повторных копиях (копирует только изменения). Не забывайте исключать node_modules:

rsync -avz --exclude node_modules ./ deploy@<ip>:/home/deploy/apps/myapp/

Переменные окружения. Секреты (токены, ключи БД) не должны лежать в git. Положите их в .env локально на сервере:

cd /home/deploy/apps/myapp
nano .env   # DATABASE_URL=..., TOKEN=...

.env должен быть в .gitignore, чтобы случайно не уехать в репозиторий.

5. Запуск приложения: PM2 или systemd

Запускать продакшен-приложение голым node server.js & или nohup - плохая идея: процесс умрёт при закрытии сессии или падении, и никто его не поднимет. Нужен менеджер, который держит процесс живым и стартует его при перезагрузке. Два стандартных варианта - PM2 и systemd.

PM2 - это отдельный демон-менеджер процессов именно для Node.js. Он даёт автоперезапуск при падении, логи, кластерный режим и команду автозапуска при ребуте. Подробное сравнение PM2 и systemd мы разбирали отдельно - в этой статье фокусируемся на PM2 как на более быстром пути для Node-проекта.[10]

Установите PM2 глобально и запустите приложение:

npm i -g pm2
cd /home/deploy/apps/myapp
npm ci
pm2 start server.js --name myapp

Лучше описать процесс в ecosystem.config.js в корне проекта, чтобы конфиг версионировался вместе с кодом:

module.exports = {
  apps: [{
    name: 'myapp',
    script: 'server.js',
    cwd: '/home/deploy/apps/myapp',
    instances: 1,
    exec_mode: 'fork',
    env: { NODE_ENV: 'production', PORT: 3000 },
  }],
};

Затем:

pm2 start ecosystem.config.js
pm2 save
pm2 startup

pm2 startup сам определяет систему инициализации (на Ubuntu это systemd) и печатает команду, которую нужно выполнить от root, чтобы PM2 поднимался при загрузке.[6] После этого приложение переживёт ребут. pm2 save запоминает текущий список процессов, чтобы PM2 восстановил их после старта.

В большинстве случаев для веб-приложения достаточно одного процесса (instances: 1, fork-режим). Кластерный режим PM2 запускает несколько процессов и балансирует между ними нагрузку без правок кода,[7] но на слабом VPS (1-2 ГБ) несколько Node-процессов часто добавляют давление на память без видимой пользы - включайте кластер только если замерили, что один процесс упирается в CPU.

Альтернатива - systemd unit. Если вы не хотите тянуть ещё один демон, можно описать сервис прямо в systemd:

[Unit]
Description=myapp
After=network.target

[Service]
User=deploy
WorkingDirectory=/home/deploy/apps/myapp
ExecStart=/usr/bin/node server.js
Restart=always
Environment=NODE_ENV=production
EnvironmentFile=/home/deploy/apps/myapp/.env

[Install]
WantedBy=multi-user.target
sudo cp myapp.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now myapp

Секрет из .env здесь не в systemd-файле (он может быть в git), а в отдельном .env, который systemd подхватывает через EnvironmentFile.[6]

6. Nginx reverse proxy на свой домен

Node-приложение обычно слушает локальный порт (например, 127.0.0.1:3000), а спереди ставится Nginx: он завершает TLS и проксирует запросы в приложение. Слушать приложению только на 127.0.0.1 - норм и даже правильнее, потому что наружу смотрит Nginx.

Установите Nginx и создайте конфигурацию сайта:

apt install -y nginx

Файл /etc/nginx/sites-available/myapp:

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

Включите и проверьте:

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

Заголовки Upgrade/Connection вместе с proxy_http_version 1.1 - это то, что позволяет WebSocket-соединениям работать через прокси; без них апгрейд соединения молча падает.[8] Не забудьте, что DNS вашего домена должен указывать на IP сервера, а в firewall порты 80/443 открыты (мы это сделали на шаге про ufw).

Проверить, что прокси работает: curl http://127.0.0.1 на сервере и открытие http://app.example.com в браузере.

7. HTTPS через Certbot (Let’s Encrypt)

Бесплатный сертификат Let’s Encrypt выдаётся через Certbot, а плагин --nginx сам правит конфигурацию Nginx, добавляя TLS и редирект с HTTP.

apt install -y certbot python3-certbot-nginx
certbot --nginx -d app.example.com

Плагин nginx автоматически получает сертификат через http-01-челлендж, устанавливает его в блок сервера и включает редирект с порта 80.[9] Ваш proxy_pass при этом не трогается - меняются только TLS-слои.

Автопродление Let’s Encrypt настроено по умолчанию через systemd-таймер, но проверка базовой команды не помешает:

certbot renew --dry-run

Проверить, что всё работает: откройте https://app.example.com и посмотрите, что браузер показывает замочек без предупреждений.

8. Rolling deploy: обновление кода без простоя

Когда нужно выкатить новую версию, обновление сводится к нескольким шагам. Разница между PM2 и systemd - только в последней команде рестарта.

cd /home/deploy/apps/myapp
git pull
npm ci
npm run build          # если есть сборка (Next.js, Vite и т.п.)
pm2 reload myapp       # или: sudo systemctl restart myapp

pm2 reload делает перезапуск безобрывным (сначала поднимает новый процесс, потом гасит старый), в отличие от pm2 restart, который гасит сразу. npm ci вместо npm install ставит зависимости строго из lockfile - воспроизводимо и без неожиданных обновлений зависимостей.

Если вы доверяете этому процессу и он уже отработал пару раз вручную, его удобно вынести в CI/CD (GitHub Actions): pull + ci + build + reload по SSH. Это отдельная тема, здесь отметим только: механическое повторение - это нормальный кандидат на автоматизацию.

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

Коротко - то, обо что реально спотыкаются, и как поймать каждую:

  • Порт не открыт в firewall. Приложение слушает 3000, но снаружи ничего нет. Проверьте ufw status и ss -tlnp на сервере; порты 80/443 должны быть открыты.
  • Приложение слушает 127.0.0.1 вместо 0.0.0.0. Для reverse proxy через Nginx это ок и даже правильно. Но если вы открываете порт напрямую во внешний мир - приложение должно слушать 0.0.0.0, иначе снаружи достучаться нельзя, и диагностируется это странными таймаутами.
  • Out-of-memory на слабых тарифах. На 1 ГБ сборка или несколько Node-процессов легко уводят сервер в своп или заставляют OOM-killer убить процесс. Лечится swap, 2 ГБ RAM или пересмотром instances.
  • Автозапуск не настроен - сервер после ребута молчит. Это самая коварная: пока вы не перезагрузитесь, всё кажется рабочим. Обязательно выполните pm2 save и pm2 startup (или systemctl enable для systemd) и после этого один раз reboot для проверки.
  • Секреты в git. Токены, ключи и строки подключения должны жить в .env, а не в ecosystem.config.js или systemd-файле, который коммитится.

10. Что в итоге

Производственная раскладка простая: VPS на 1-2 ГБ NVMe, SSH по ключу, Node 26 Active LTS, приложение под PM2 с автозапуском, Nginx спереди завершает TLS, сертификат Let’s Encrypt обновляется сам. Весь путь первый раз - примерно полчаса, дальше обновления - это git pull + npm ci + pm2 reload.

Часть ссылок на этой странице - партнёрские. На наши рекомендации они не влияют - см. дисклеймер о партнёрских ссылках.

Источники

  1. Timeweb Cloud - VDS/VPS на Linux (тарифы Cloud-40 2800 руб/мес, Cloud-50 3840 руб/мес - цены Timeweb Cloud пересверены 24.10.2026: Санкт-Петербург, Premium 3.3 ГГц; публичный IPv4 тарифицируется отдельно - 150 руб/мес за адрес, IPv6 бесплатно - пересверено 29.10.2026; почасовой биллинг, образы Ubuntu/Debian). https://timeweb.cloud/services/vps-linux - проверено 2026-10-08
  2. Beget - VPS/VDS (линейный конфигуратор с 09.04.2026: базовая конфигурация 2 ядра / 2 ГБ / 30 ГБ NVMe 38,44 руб/день, публичный IPv4 5 руб/день, маркетплейс с Ubuntu 26.04/24.04, KVM без оверселлинга). https://beget.com/ru/vps - проверено 2026-10-08; цены Beget пересверены 24.10.2026
  3. Node.js - официальный график релизов и статус LTS (Node 26 - Active LTS с 28.10.2026, v26.13.0; Node 24 - Maintenance LTS с 20.10.2026, EOL 2028-04-30; Node 22 - Maintenance). https://nodejs.org/en/about/previous-releases; https://endoflife.date/nodejs - проверено 2026-10-30
  4. nvm - Node Version Manager, официальная установка через install.sh (версия v0.40.8). https://github.com/nvm-sh/nvm - проверено 2026-10-08
  5. NodeSource - официальные binary distributions Node.js (установка через setup_XX.x скрипт и apt). https://github.com/nodesource/distributions - проверено 2026-10-08
  6. PM2 - Startup Script (pm2 startup, pm2 save, systemd-интеграция, автозапуск при ребуте). https://pm2.keymetrics.io/docs/usage/startup/ - проверено 2026-10-08
  7. PM2 - Cluster Mode (несколько процессов и балансировка без правок кода). https://pm2.keymetrics.io/docs/usage/cluster-mode/ - проверено 2026-10-08
  8. nginx - модуль ngx_http_proxy_module (proxy_pass, proxy_set_header, WebSocket-проксирование). https://nginx.org/en/docs/http/ngx_http_proxy_module.html - проверено 2026-10-08
  9. Certbot - официальная документация (плагин nginx, автоустановка и продление). https://eff-certbot.readthedocs.io/en/stable/using.html - проверено 2026-10-08
  10. DeploySmith - PM2 против systemd для Node.js (сравнение подходов к запуску процесса). https://deploysmith.dev/ru/hosting/pm2-vs-systemd/ - проверено 2026-10-08
  11. Ubuntu - релиз Ubuntu 26.04 LTS (анонс Canonical, 23.04.2026). https://ubuntu.com/blog - проверено 2026-10-08