- Node.js на сервер ставят из репозитория NodeSource или через nvm: пакет
nodejsиз стандартного репозитория Ubuntu и Debian обычно заметно старше актуальной LTS-версии. - PM2 держит Node.js-приложение запущенным круглосуточно:
pm2 startзапускает процесс,pm2 saveзапоминает список процессов,pm2 startupподнимает их после перезагрузки сервера. - Node.js-приложение слушает локальный порт (например, 3000), а наружу на портах 80 и 443 смотрит nginx как обратный прокси - в нём же настраивается HTTPS.
pm2 reloadв режиме cluster перезапускает процессы Node.js по одному, поэтому обновление кода проходит без простоя, аpm2 restartиsystemctl restartдают короткий перерыв.- Небольшому API или сайту на Node.js хватает VPS с 1 vCPU и 2 ГБ памяти - на Tihost такая конфигурация стоит $4.00 в месяц.
Шаг 1. Установите актуальную LTS-версию Node.js
LTS (Long Term Support) - ветка Node.js с долгой поддержкой и исправлениями безопасности, для сервера берут именно её. Основной путь - репозиторий NodeSource: скрипт setup_lts.x подключает его для текущей LTS-ветки, после чего Node.js и npm ставятся и обновляются обычным apt. Скрипт сначала скачивается в файл - его можно прочитать перед запуском:
sudo apt update
sudo apt install -y ca-certificates curl git
curl -fsSL https://deb.nodesource.com/setup_lts.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs
node -v
npm -vВторой путь - nvm (Node Version Manager): он ставит Node.js в домашний каталог пользователя командой nvm install --lts и удобен, когда на одном сервере нужны разные версии. Минус для сервера - путь к node зависит от пользователя и версии, и его приходится прописывать в systemd и cron вручную. Номера LTS-веток и график поддержки - на nodejs.org.
Шаг 2. Создайте отдельного пользователя для приложения
Приложение не должно работать от root: если в нём найдут уязвимость, атакующий получит права только этого пользователя. Пользователь app без пароля не войдёт по SSH сам, а вы переключаетесь в него через sudo -iu app:
sudo adduser --disabled-password --gecos "" app
sudo -iu appШаг 3. Загрузите код приложения
Код удобнее всего доставлять через git: клонируете репозиторий один раз, дальше обновляете git pull. npm ci ставит зависимости строго по package-lock.json, а npm run build --if-present собирает проект, если в нём есть скрипт build (TypeScript, Next.js, Nest):
git clone https://github.com/you/myapp.git ~/myapp
cd ~/myapp
npm ci
npm run build --if-presentДля приватного репозитория создайте пользователю app SSH-ключ (ssh-keygen -t ed25519) и добавьте публичную часть в репозиторий как deploy key только на чтение - так серверу не нужен ваш личный пароль или токен. Подробнее о ключах - в статье про подключение к VPS по SSH.
Шаг 4. Вынесите настройки и секреты в .env
Пароли, токены и адреса баз данных не хранят в коде и в git - их кладут в файл .env, который может прочитать только владелец. Добавьте .env в .gitignore, чтобы он случайно не попал в репозиторий:
cat > ~/myapp/.env <<'EOF'
NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://myapp:password@localhost:5432/myapp
EOF
chmod 600 ~/myapp/.envПриложение должно само прочитать .env: чаще всего это пакет dotenv (npm install dotenv и require("dotenv").config() первой строкой), а в Node.js 20.6 и новее есть встроенный флаг --env-file=.env. При запуске через systemd файл подставляет сам systemd - об этом ниже. Если нужна база данных, её установка и настройка доступа описаны в статье про PostgreSQL на VPS.
Шаг 5. Запустите приложение под PM2
PM2 - менеджер процессов для Node.js: он перезапускает упавшее приложение, собирает логи и умеет перезагружать процессы по одному. Поставьте его глобально от пользователя с sudo:
sudo npm install -g pm2Запуск, сохранение списка процессов и автозапуск. -i 2 включает режим cluster с двумя процессами - это нужно для обновления без простоя даже на сервере с 1 vCPU. pm2 startup создаёт сервис systemd pm2-app, который после перезагрузки сервера восстановит процессы из pm2 save:
sudo -iu app
cd ~/myapp
pm2 start server.js --name myapp -i 2
pm2 save
exit
sudo pm2 startup systemd -u app --hp /home/appАльтернатива PM2: сервис systemd
Если одного процесса достаточно, PM2 можно не ставить: systemd уже есть в Ubuntu и Debian и делает то же самое - запускает приложение при загрузке и перезапускает через 5 секунд после падения. EnvironmentFile передаёт переменные из .env в окружение процесса, dotenv при этом не нужен. Минус - systemctl restart останавливает приложение на время перезапуска:
sudo tee /etc/systemd/system/myapp.service > /dev/null <<'EOF'
[Unit]
Description=Node.js app
After=network-online.target
Wants=network-online.target
[Service]
User=app
WorkingDirectory=/home/app/myapp
EnvironmentFile=/home/app/myapp/.env
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now myappШаг 6. Проверьте статус и логи
pm2 status показывает процессы, их память и число перезапусков, pm2 logs - вывод приложения. Модуль pm2-logrotate ротирует логи, чтобы они не заняли весь диск. Для варианта с systemd то же самое дают systemctl status myapp и journalctl -u myapp -f. Команды PM2 выполняйте от пользователя app:
pm2 status
pm2 logs myapp --lines 100
pm2 install pm2-logrotateШаг 7. Поставьте nginx перед приложением
Обратный прокси (reverse proxy) принимает запросы из интернета и передаёт их приложению на локальный порт. nginx берёт на себя HTTPS, сжатие, статику и лимиты, а порт 3000 остаётся закрытым снаружи. Заголовки Upgrade и Connection нужны для WebSocket. Сохраните конфиг как /etc/nginx/sites-available/myapp, заменив example.com на свой домен:
server {
listen 80;
server_name example.com;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/myapp
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo ufw allow 80,443/tcpДомен должен указывать на IP сервера - как это настроить, описано в статье про привязку домена к VPS. Сертификат Let's Encrypt и редирект на HTTPS добавляются за пару команд - по инструкции nginx и Let's Encrypt.
Шаг 8. Обновляйте приложение без простоя
pm2 reload в режиме cluster поднимает новый процесс, дожидается его запуска и только потом останавливает старый - и так по очереди для каждого. Пользователи не видят ошибок, пока выкатывается новая версия:
sudo -iu app
cd ~/myapp
git pull
npm ci
npm run build --if-present
pm2 reload myappКакой VPS нужен Node.js-приложению
API, Telegram-боту или небольшому сайту на Node.js хватает 1 vCPU и 2 ГБ памяти - на Tihost это конфигурация Starter за $4.00 в месяц. Если на том же сервере работают база данных и сборка фронтенда, берите 4 ГБ и больше: подробнее о расчёте - в статье сколько оперативной памяти нужно VPS. Серверы Tihost работают на KVM с NVMe и полным root-доступом и разворачиваются примерно за 2 минуты после оплаты.
AMD Ryzen 9, NVMe и защита от DDoS в Германии, Финляндии и Польше. Оплата криптовалютой или картой.
Частые вопросы
Что лучше для Node.js на сервере - PM2 или systemd?
PM2 удобнее, когда нужны режим cluster и обновление без простоя через pm2 reload. systemd уже встроен в Ubuntu и Debian и проще для одного процесса, но systemctl restart даёт короткий перерыв в работе.
Как поставить Node.js LTS на Ubuntu?
Подключите репозиторий NodeSource скриптом setup_lts.x и выполните sudo apt install -y nodejs. Альтернатива - nvm и команда nvm install --lts в домашнем каталоге пользователя.
Почему Node.js-приложение останавливается, когда я закрываю SSH?
Приложение, запущенное командой node server.js из терминала, завершается вместе с SSH-сессией. Запустите его под PM2 или как сервис systemd - тогда оно работает независимо от SSH и поднимается после перезагрузки сервера.
Нужен ли nginx, если Node.js сам умеет слушать порт?
nginx перед Node.js-приложением отвечает за HTTPS, сжатие, статику и несколько сайтов на одном IP, а порт приложения остаётся закрытым снаружи. Без nginx пришлось бы запускать Node.js с правами на порты 80 и 443 и настраивать сертификаты в коде.
Как обновить Node.js-приложение без простоя?
Запустите приложение под PM2 в режиме cluster (pm2 start server.js -i 2), а после git pull и npm ci выполните pm2 reload myapp: PM2 перезапустит процессы по одному, и запросы продолжат обслуживаться.
Сколько памяти нужно Node.js-приложению на VPS?
Небольшому API или боту на Node.js хватает VPS с 2 ГБ памяти. Если рядом работает база данных или на сервере собирается фронтенд, удобнее 4-8 ГБ.