Reverse proxy PM2 + Nginx: podłącz aplikację Node.js do portu 80
Na serwerze Ubuntu 24.04 skonfigurowaliśmy reverse proxy Node.js z PM2 i Nginx: PM2 odpowiada za uruchamianie aplikacji, a Nginx przekazuje do aplikacji żądania przychodzące na port 80. Sprawdziliśmy też, że PM2 ponownie uruchamia aplikację nawet po wymuszonym zakończeniu jej procesu lub restarcie serwera.
Poniższe pola z wynikami pokazują dane wyjściowe poleceń uruchomionych jako zwykły użytkownik deploy. W miejscu domeny jest przykładowa wartość example.com, a żądania wysłano z wnętrza serwera z nagłówkiem Host.
1️⃣ Instalacja Node.js
Node.js instaluje się, dodając repozytorium NodeSource do apt, a jako wersję wybraliśmy Node.js 26, czyli wersję LTS na listopad 2026.
🔲 NodeSource setup_26.x
export DEBIAN_FRONTEND=noninteractiveJeśli najpierw ustawisz tę wartość, podczas instalacji pakietów nie pojawi się okno z pytaniem o ustawienia. Opcja -E polecenia sudo poniżej przekazuje tę wartość także do polecenia uruchamianego z uprawnieniami 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 -yGdy skrypt skończy dodawać repozytorium, możesz zainstalować Node.js przez apt jak każdy pakiet 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 jest w pakiecie nodejs, więc nie musisz instalować go osobno.
2️⃣ Testowa aplikacja Node.js
Aby sprawdzić, czy PM2 i Nginx są dobrze połączone, utwórz testową aplikację, która po otrzymaniu żądania zwraca jedną linię tekstu.
🔲 app.js
mkdir -p ~/helloUtwórz folder hello w swoim folderze domowym i zapisz w nim plik 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');
APPAplikacja przyjmuje żądania tylko na porcie 3000 adresu 127.0.0.1, więc nie da się jej otworzyć bezpośrednio spoza serwera. Nginx skonfigurujemy tak, aby odbierał żądania z zewnątrz i przekazywał je na ten port.
3️⃣ Uruchamianie aplikacji przez PM2 i autostart
PM2 to menedżer procesów, który uruchamia aplikacje Node.js w tle i ponownie uruchamia aplikację, gdy jej proces się zatrzyma. W tej części konfigurujemy też autostart, który ponownie uruchamia aplikację przy rozruchu serwera.
🔲 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.2Przy pierwszym poleceniu pm2 uruchamia się też demon PM2, a zainstalowane PM2 ma wersję 7.1.2.
🔲 pm2 start
cd ~/helloPrzejdź do folderu z plikiem app.js, a potem uruchom aplikację przez 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 to nazwa aplikacji widoczna na liście PM2, a kolejne polecenia wskazują aplikację tą nazwą.
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 w kolumnie status oznacza, że aplikacja działa, a kolumna ↺ to liczba ponownych uruchomień aplikacji przez PM2.
🔲 curl 127.0.0.1:3000
Gdy wyślesz żądanie z wnętrza serwera, linia tekstu zwrócona przez aplikację pojawi się bez zmian.
curl -sS http://127.0.0.1:3000/hello from node🔲 pm2 restart
Po zmianie kodu aplikacji uruchom ją ponownie, aby zastosować zmianę.
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/sPo ponownym uruchomieniu pid zmienił się z 17257 na 17306, a kolumna ↺ wzrosła do 1.
🔲 kill -9
Nagłe zatrzymanie procesu aplikacji można wywołać poleceniem kill -9, a pm2 pid wypisuje numer procesu aplikacji.
kill -9 "$(pm2 pid hello)"Po 3 sekundach sprawdź ponownie listę 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 ponownie uruchomiło aplikację z nowym pid 17797 i była ona online, a kolumna ↺ wzrosła do 2.
🔲 pm2 save
pm2 save[PM2] Saving current process list...
[PM2] Successfully saved in /home/deploy/.pm2/dump.pm2pm2 save zapisuje listę działających aplikacji w pliku dump.pm2, a przy rozruchu PM2 ponownie uruchamia aplikacje według tej listy.
🔲 pm2 startup
Aby PM2 działało przy rozruchu, zarejestruj PM2 jako usługę systemd. Poniższe polecenie tworzy usługę z nazwą bieżącego konta i jego folderem domowym.
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 to część, dzięki której pm2 uruchamiane z uprawnieniami root znajduje ścieżkę node bieżącego konta, a -u i --hp to konto i folder domowy, z którymi działa usługa. Wiersz ExecStart w pliku usługi to pm2 resurrect, które przy rozruchu przywraca zapisaną listę aplikacji.
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 oznacza, że usługa uruchamia się przy rozruchu. pm2 startup tylko rejestruje usługę i nie uruchamia jej teraz, dlatego widać inactive (dead), a usługa działa od następnego rozruchu.
🔲 pm2 list po restarcie
sudo systemctl rebootPo około 2 minutach połącz się ponownie jako deploy i sprawdź stan aplikacji.
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/sDwie minuty po włączeniu serwera aplikacja była już online od 108 sekund: PM2 uruchomiło ją ponownie razem z rozruchem.
4️⃣ Konfiguracja reverse proxy w Nginx
Ponieważ aplikacja przyjmuje żądania tylko na 127.0.0.1:3000, Nginx odbiera żądania przychodzące na port 80 i przekazuje je do aplikacji. Taki układ, w którym serwer z przodu odbiera żądania i przekazuje je do serwera z tyłu, nazywa się reverse proxy.
🔲 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 z repozytorium Ubuntu 24.04 ma wersję 1.24.0 i przy instalacji od razu rejestruje się do uruchamiania przy rozruchu.
🔲 /etc/nginx/sites-available/hello
Utwórz plik /etc/nginx/sites-available/hello i wpisz do niego poniższą treść.
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 to adres aplikacji, do której przekazywane są żądania. Nagłówki Host, X-Real-IP, X-Forwarded-For i X-Forwarded-Proto to wartości przekazywane po to, aby aplikacja znała domenę pierwotnego żądania, IP odwiedzającego oraz to, czy było to http, czy https.
proxy_http_version 1.1 oraz nagłówki Upgrade i Connection są potrzebne, aby przekazać połączenia WebSocket aż do aplikacji. Nagłówki Upgrade i Connection po przejściu przez proxy nie trafiają do aplikacji, dlatego wpisuje się je bezpośrednio w konfiguracji.
sudo ln -sf /etc/nginx/sites-available/hello /etc/nginx/sites-enabled/helloNginx czyta tylko te konfiguracje, dla których w folderze sites-enabled jest dowiązanie.
🔲 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 nginxNajpierw sprawdź składnię konfiguracji poleceniem nginx -t, a jeśli przejdzie, zastosuj nową konfigurację przez reload bez zatrzymywania Nginx.
🔲 Żądanie curl z nagłówkiem 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: 16Po ponownym wczytaniu konfiguracji na żądanie z nagłówkiem Host wróciła odpowiedź aplikacji. Wiersz Server w nagłówkach odpowiedzi to nginx, a Content-Length 16 ma ten sam rozmiar co linia hello from node zwracana przez aplikację (16 bajtów ze znakiem nowej linii). Aby łączyć się spoza serwera, port 80 musi być otwarty w zaporze.
5️⃣ Włączenie HTTPS przez Certbot
Wtyczka Nginx w Certbot pozwala za jednym razem uzyskać certyfikat i zmienić konfigurację Nginx. Uruchom poniższe polecenie, gdy domena wskazuje już na IP serwera.
sudo certbot --nginxInstalację Certbot i test automatycznego odnawiania opisuje artykuł poniżej; polecenie wydania certyfikatu z tamtego artykułu jest dla Apache, więc zamień tylko krok wydania na polecenie powyżej.
🔢 FAQ & polecane treści
Logi usług
Polecenie journalctl: użycie, logi według usług i porządkowanie rozmiaru dziennika
Artykuł o tym, jak czytać logi usług systemd, takich jak nginx i pm2-deploy, osobno dla każdej usługi.
ℹ️ Informacja o linkach partnerskich
Treści tej witryny zawierają linki partnerskie. Jeśli odwiedzający kupi produkt lub usługę przez taki link, witryna otrzymuje od sprzedawcy prowizję. Kwota płacona przy tym przez kupującego (podczas promocji spada↓) nie wzrasta. Podane ceny, rabaty i dostępność odnoszą się do chwili pisania i mogą różnić się od stanu faktycznego, dlatego przed zakupem sprawdź je u sprzedawcy. Produkty wybieramy i oceniamy według własnych kryteriów, a prowizja nie wpływa ani na kolejność prezentacji, ani na treść ocen.