Антон Ларичев

Введение
Деплой Node.js приложения на VPS — навык, без которого невозможно представить современного backend-разработчика. В отличие от платформ вроде Vercel или Heroku, собственный VPS даёт полный контроль над окружением, экономит деньги при росте трафика и позволяет тонко настраивать инфраструктуру. В этой статье разберём весь путь: от подключения к чистому серверу Ubuntu 22.04 до запуска приложения с автоперезапуском, обратным прокси и HTTPS.
Для работы понадобится VPS любого провайдера (Timeweb, Selectel, Hetzner, DigitalOcean), доменное имя, направленное A-записью на IP сервера, и Node.js-приложение в Git-репозитории. Будем считать, что у вас уже есть root-доступ по SSH.
Первичная настройка сервера
Первое подключение всегда выполняется под пользователем root, но работать под ним постоянно — плохая практика. Создадим отдельного пользователя с правами sudo.
# Подключаемся к серверу
ssh root@123.45.67.89
# Создаём пользователя deploy
adduser deploy
# Добавляем его в группу sudo
usermod -aG sudo deploy
# Копируем SSH-ключи, чтобы войти без пароля
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
Дальше обновим систему и поставим базовые утилиты. На свежем сервере это занимает пару минут.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl git build-essential ufw
Настроим firewall: разрешим SSH, HTTP и HTTPS, остальное закроем.
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Установка Node.js через nvm
Пакет node из apt почти всегда устарел. Используем nvm — это даёт возможность держать несколько версий и быстро переключаться между ними.
# Устанавливаем nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# Перезагружаем переменные окружения
source ~/.bashrc
# Ставим актуальную LTS-версию
nvm install --lts
nvm use --lts
# Проверяем
node -v
npm -v
Клонирование и сборка приложения
Клонируем репозиторий в домашнюю папку пользователя deploy. Если репозиторий приватный, заранее добавьте SSH-ключ сервера в GitHub.
cd ~
git clone git@github.com:username/my-app.git
cd my-app
# Устанавливаем зависимости только для продакшена
npm ci --omit=dev
# Если есть сборка — выполняем её
npm run build
Не забудьте про переменные окружения. Создайте файл .env в корне проекта и пропишите туда секреты. Никогда не коммитьте этот файл в репозиторий.
nano .env
NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
Запуск через PM2
Запускать node index.js напрямую — путь в никуда: процесс упадёт, сервер перезагрузится, и ваше приложение останется лежать. PM2 решает эту проблему: он следит за процессом, перезапускает его при падении и стартует автоматически после ребута сервера.
# Ставим PM2 глобально
npm install -g pm2
# Запускаем приложение
pm2 start npm --name "my-app" -- start
# Сохраняем список процессов
pm2 save
# Генерируем systemd-юнит для автозапуска
pm2 startup
Команда pm2 startup выведет строку, которую нужно скопировать и выполнить от sudo — она зарегистрирует PM2 в systemd.
Полезные команды для отладки:
pm2 logs my-app # смотрим логи в реальном времени
pm2 monit # интерактивный монитор CPU и памяти
pm2 restart my-app # перезапуск после обновления кода
pm2 list # список всех процессов
Настройка Nginx как обратного прокси
Node.js слушает на порту 3000, но открывать его наружу нельзя — это и небезопасно, и неудобно. Nginx будет принимать запросы на 80/443 и проксировать их в приложение.
sudo apt install -y nginx
Создаём конфиг для нашего сайта:
sudo nano /etc/nginx/sites-available/my-app
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://localhost: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;
proxy_cache_bypass $http_upgrade;
}
}
Активируем конфиг и проверяем синтаксис:
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Бесплатный SSL через Let's Encrypt
HTTPS сегодня обязателен: без него браузеры показывают предупреждения, а поисковики занижают позиции. Certbot автоматизирует получение и продление сертификата.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
Certbot сам отредактирует конфиг Nginx, добавит редирект с HTTP на HTTPS и настроит cron для автопродления. Проверить можно командой:
sudo certbot renew --dry-run
Частые ошибки
Порт 3000 открыт наружу. Если забыли настроить firewall или явно открыли порт через ufw, приложение становится доступно в обход Nginx. Всегда биндьте Node.js на 127.0.0.1, а не на 0.0.0.0.
Запуск под root. Никогда не запускайте Node.js от имени root — уязвимость в зависимостях даёт атакующему полный контроль над сервером. Используйте отдельного пользователя.
Игнорирование логов. PM2 пишет логи в ~/.pm2/logs/, и они растут без ограничений. Подключите ротацию модулем pm2-logrotate, иначе через месяц диск кончится.
Деплой без процесс-менеджера. Запуск через nohup node index.js & кажется простым, но при падении приложение не поднимется. PM2 или systemd — обязательны.
Отсутствие swap. На VPS с 1 ГБ RAM сборка фронтенда часто валится с OOM. Добавьте 2 ГБ swap-файла командой fallocate -l 2G /swapfile, и сборка перестанет падать.
Использование npm install вместо npm ci. В продакшене всегда используйте npm ci — он ставит зависимости строго по lock-файлу и работает быстрее.
Заключение
За полчаса мы прошли путь от чистого VPS до полноценного продакшен-окружения: безопасный пользователь, актуальная Node.js, автоперезапуск через PM2, обратный прокси Nginx и HTTPS от Let's Encrypt. Эта схема выдерживает сотни тысяч запросов в день и подходит для большинства проектов — от лендингов до полноценных SaaS.
Следующие шаги — настройка CI/CD через GitHub Actions для автоматического деплоя по push в main, мониторинг через Grafana и Loki, а также бэкапы базы данных. Но это уже темы отдельных статей. Главное — у вас есть рабочая основа, которую можно развивать.






Комментарии
0