Обратный прокси PM2 + Nginx: подключение приложения Node.js к порту 80
На сервере Ubuntu 24.04 мы развернули обратный прокси Node.js с PM2 и Nginx: PM2 отвечает за запуск приложения, а Nginx передаёт приложению запросы, приходящие на порт 80. Мы также убедились, что PM2 снова запускает приложение, даже если процесс приложения принудительно завершён или сервер перезагружен.
Блоки с результатами ниже — это вывод команд, выполненных от обычного пользователя deploy. Вместо домена стоит пример example.com, а запросы отправлялись изнутри сервера с заголовком Host.
1️⃣ Установка Node.js
Node.js устанавливается после добавления репозитория NodeSource в apt; мы выбрали Node.js 26 — версию LTS на ноябрь 2026 года.
🔲 NodeSource setup_26.x
export DEBIAN_FRONTEND=noninteractiveЕсли сначала задать это значение, во время установки пакетов не появится окно с вопросами о настройках. Опция -E у команды sudo ниже передаёт это значение и команде, которая выполняется с правами root.
curl -fsSL https://deb.nodesource.com/setup_26.x | sudo -E bash -2026-11-07 16:08:16 - Repository configured successfully.
2026-11-07 16:08:16 - To install Node.js, run: apt install nodejs -yКогда скрипт закончит добавлять репозиторий, вы сможете установить Node.js через apt, как любой пакет Ubuntu.
🔲 apt-get install nodejs
sudo -E apt-get install -y nodejsSetting up libatomic1:amd64 (14.2.0-4ubuntu2~24.04.1) ...
Setting up nodejs (26.13.0-1nodesource1) ...
Processing triggers for libc-bin (2.39-0ubuntu8.9) ...
Processing triggers for man-db (2.12.0-4build2) ...🔲 node -v, npm -v
node -vv26.13.0npm -v11.20.0npm входит в пакет nodejs, поэтому отдельно его устанавливать не нужно.
2️⃣ Тестовое приложение Node.js
Чтобы проверить, правильно ли связаны PM2 и Nginx, создайте тестовое приложение, которое в ответ на запрос возвращает одну строку текста.
🔲 app.js
mkdir -p ~/helloСоздайте папку hello в домашней папке и запишите в неё файл app.js.
cat > ~/hello/app.js <<'APP'
const http = require('http');
http.createServer((req, res) => {
console.log(req.method, req.url);
res.end('hello from node\n');
}).listen(3000, '127.0.0.1');
APPПриложение принимает запросы только на порту 3000 адреса 127.0.0.1, поэтому напрямую извне сервера его не открыть. Мы настроим Nginx так, чтобы он принимал внешние запросы и передавал их на этот порт.
3️⃣ Запуск приложения через PM2 и автозапуск
PM2 — это менеджер процессов, который запускает приложения Node.js в фоне и снова запускает приложение, когда его процесс завершается. В этом разделе мы также настроим автозапуск, который поднимает приложение при загрузке сервера.
🔲 npm install -g pm2
sudo npm install -g pm2added 45 packages in 5spm2 -v[PM2] Spawning PM2 daemon with pm2_home=/home/deploy/.pm2
[PM2] PM2 Successfully daemonized
7.1.2При первом запуске команды pm2 вместе с ней стартует демон PM2; установленная версия PM2 — 7.1.2.
🔲 pm2 start
cd ~/helloПерейдите в папку с app.js и запустите приложение через PM2.
pm2 start app.js --name hello[PM2] Starting /home/deploy/hello/app.js in fork_mode (1 instance)
[PM2] Done.
┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ hello │ default │ N/A │ fork │ 17257 │ 0s │ 0 │ online │ 0% │ 26.3mb │ deploy │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘--name — это имя приложения в списке PM2; дальнейшие команды указывают приложение по этому имени.
pm2 list┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ hello │ default │ N/A │ fork │ 17257 │ 2s │ 0 │ online │ 0% │ 58.5mb │ deploy │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 0% | ram usage: 38.8% | disk: ⇓ 0mb/s ⇑ 0mb/sonline в столбце status означает, что приложение работает, а столбец ↺ показывает, сколько раз PM2 перезапускал приложение.
🔲 curl 127.0.0.1:3000
Если отправить запрос изнутри сервера, строка текста, которую возвращает приложение, придёт без изменений.
curl -sS http://127.0.0.1:3000/hello from node🔲 pm2 restart
После изменения кода приложения перезапустите его, чтобы изменения вступили в силу.
pm2 restart helloUse --update-env to update environment variables
[PM2] Applying action restartProcessId on app [hello](ids: [ 0 ])
[PM2] [hello](0) ✓
┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ hello │ default │ N/A │ fork │ 17306 │ 0s │ 1 │ online │ 0% │ 19.3mb │ deploy │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 0% | ram usage: 38.8% | disk: ⇓ 0mb/s ⇑ 0mb/spm2 list┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ hello │ default │ N/A │ fork │ 17306 │ 2s │ 1 │ online │ 0% │ 58.3mb │ deploy │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 0% | ram usage: 38.8% | disk: ⇓ 0mb/s ⇑ 0mb/sПосле перезапуска pid сменился с 17257 на 17306, а столбец ↺ вырос до 1.
🔲 kill -9
Внезапную остановку процесса приложения можно смоделировать командой kill -9, а pm2 pid выводит номер процесса приложения.
kill -9 "$(pm2 pid hello)"Через 3 секунды снова проверьте список PM2.
pm2 list┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ hello │ default │ N/A │ fork │ 17797 │ 3s │ 2 │ online │ 0% │ 58.6mb │ deploy │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 51.2% | ram usage: 35.8% | eth0: ⇓ 1kb/s ⇑ 1kb/s | eth1: ⇓ 19kb/s ⇑ 0mb/s | disk: ⇓ 728kb/s ⇑ 3.1mb/sPM2 снова запустил приложение с новым pid 17797, оно было в состоянии online, а столбец ↺ вырос до 2.
🔲 pm2 save
pm2 save[PM2] Saving current process list...
[PM2] Successfully saved in /home/deploy/.pm2/dump.pm2pm2 save сохраняет список запущенных приложений в файл dump.pm2, и при загрузке PM2 снова запускает приложения по этому списку.
🔲 pm2 startup
Чтобы PM2 запускался при загрузке, зарегистрируйте PM2 как службу systemd. Команда ниже создаёт службу с именем текущей учётной записи и её домашней папкой.
sudo env PATH="$PATH" pm2 startup systemd -u "$(id -un)" --hp "$HOME"ExecStart=/usr/lib/node_modules/pm2/bin/pm2 resurrect
ExecReload=/usr/lib/node_modules/pm2/bin/pm2 reload all
ExecStop=/usr/lib/node_modules/pm2/bin/pm2 kill
[Install]
WantedBy=multi-user.target
Target path
/etc/systemd/system/pm2-deploy.service
Command list
[ 'systemctl enable pm2-deploy' ]
[PM2] Writing init configuration in /etc/systemd/system/pm2-deploy.service
[PM2] Making script booting at startup...
[PM2] [-] Executing: systemctl enable pm2-deploy...
Created symlink /etc/systemd/system/multi-user.target.wants/pm2-deploy.service → /etc/systemd/system/pm2-deploy.service.
[PM2] [v] Command successfully executed.env PATH — это часть, которая позволяет pm2, запущенному с правами root, найти путь к node текущей учётной записи, а -u и --hp — учётная запись и домашняя папка, от которых работает служба. Строка ExecStart в файле службы — это pm2 resurrect, который при загрузке восстанавливает сохранённый список приложений.
systemctl status pm2-deploy --no-pager | head -n 5○ pm2-deploy.service - PM2 process manager
Loaded: loaded (/etc/systemd/system/pm2-deploy.service; enabled; preset: enabled)
Active: inactive (dead)
Docs: https://pm2.keymetrics.io/enabled означает, что служба запускается при загрузке. pm2 startup только регистрирует службу и не запускает её сейчас, поэтому видно inactive (dead), а служба начнёт работать со следующей загрузки.
🔲 pm2 list после перезагрузки
sudo systemctl rebootПримерно через 2 минуты снова подключитесь как deploy и проверьте состояние приложения.
uptime -pup 2 minutespm2 list┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ hello │ default │ N/A │ fork │ 976 │ 108s │ 0 │ online │ 0% │ 63.0mb │ deploy │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 0.3% | ram usage: 35.1% | disk: ⇓ 4kb/s ⇑ 4kb/sЧерез 2 минуты после включения сервера приложение уже 108 секунд было online: PM2 снова запустил его вместе с загрузкой.
4️⃣ Настройка обратного прокси Nginx
Так как приложение принимает запросы только на 127.0.0.1:3000, запросы, приходящие на порт 80, принимает Nginx и передаёт приложению. Такая схема, при которой сервер впереди принимает запросы и пересылает их серверу позади, называется обратным прокси.
🔲 apt-get install nginx
sudo -E apt-get install -y nginxSetting up nginx-common (1.24.0-2ubuntu7.19) ...
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.
Setting up nginx (1.24.0-2ubuntu7.19) ...nginx -vnginx version: nginx/1.24.0 (Ubuntu)Nginx из репозитория Ubuntu 24.04 имеет версию 1.24.0 и при установке сразу регистрируется для запуска при загрузке.
🔲 /etc/nginx/sites-available/hello
Создайте файл /etc/nginx/sites-available/hello и поместите в него содержимое ниже.
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";
}
}proxy_pass — это адрес приложения, которому передаются запросы. Заголовки Host, X-Real-IP, X-Forwarded-For и X-Forwarded-Proto — значения, которые передаются, чтобы приложение знало домен исходного запроса, IP посетителя и был ли запрос по http или https.
proxy_http_version 1.1 и заголовки Upgrade и Connection нужны, чтобы передавать соединения WebSocket до самого приложения. Заголовки Upgrade и Connection не доходят до приложения после прохождения через прокси, поэтому их задают прямо в настройках.
sudo ln -sf /etc/nginx/sites-available/hello /etc/nginx/sites-enabled/helloNginx читает только те настройки, для которых в папке sites-enabled создана ссылка.
🔲 nginx -t
sudo nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfulsudo systemctl reload nginxСначала проверьте синтаксис настроек командой nginx -t и, если проверка пройдена, примените новые настройки через reload, не останавливая Nginx.
🔲 Запрос curl с заголовком Host
curl -sS -i -H 'Host: example.com' http://127.0.0.1/ | head -n 4HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Sat, 07 Nov 2026 08:09:38 GMT
Content-Length: 16После перечитывания настроек запрос с заголовком Host получил ответ приложения. Строка Server в заголовках ответа — nginx, а Content-Length 16 совпадает по размеру со строкой hello from node, которую возвращает приложение (16 байт с переводом строки). Чтобы подключаться извне сервера, порт 80 должен быть открыт в брандмауэре.
5️⃣ Подключение HTTPS через Certbot
С плагином Nginx для Certbot можно за один раз выпустить сертификат и изменить настройки Nginx. Выполните команду ниже, когда домен уже указывает на IP сервера.
sudo certbot --nginxУстановка Certbot и проверка автоматического продления описаны в статье ниже; команда выпуска в той статье рассчитана на Apache, поэтому замените только шаг выпуска на команду выше.
🔢 FAQ & рекомендуемые материалы
Журналы служб
Команда journalctl: использование, журналы по службам и очистка размера журнала
Статья о том, как читать журналы служб systemd, таких как nginx и pm2-deploy, по каждой службе отдельно.
ℹ️ О партнёрских ссылках
Материалы этого сайта содержат партнёрские ссылки. Если посетитель покупает товар или услугу по такой ссылке, сайт получает комиссию от продавца. При этом сумма, которую платит покупатель (при акционных скидках сумма снижается ↓), не увеличивается. Указанные цены, скидки и наличие актуальны на момент написания и могут отличаться, поэтому перед покупкой уточняйте их у продавца. Товары выбираются и оцениваются по нашим собственным критериям, а комиссия не влияет ни на порядок, ни на содержание обзоров.