PM2 + Nginx リバースプロキシ:Node.js アプリを 80番ポートにつなぐ
Ubuntu 24.04 サーバーに Node.js PM2 Nginx のリバースプロキシ構成を載せ、アプリの実行は PM2 が担当し、80番ポートに届いたリクエストは Nginx がアプリへ渡すように設定しました。アプリのプロセスを強制終了したりサーバーを再起動したりしても、PM2 がアプリを再び起動するところまで確認しました。
以下の結果ボックスは一般ユーザー deploy で実行した出力です。ドメインの部分には例の値 example.com を置き、サーバー内から Host ヘッダーを付けてリクエストしました。
1️⃣ Node.js のインストール
Node.js は NodeSource のリポジトリを apt に登録してインストールし、バージョンは 2026年11月時点の LTS である Node.js 26 にしました。
🔲 NodeSource setup_26.x
export DEBIAN_FRONTEND=noninteractiveこの値を先に置くと、パッケージのインストール中に設定を尋ねる画面が出ません。下の sudo コマンドの -E オプションは、この値を 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 を Ubuntu のパッケージと同じように apt でインストールできます。
🔲 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 が正しくつながるかを見るため、リクエストを受けると文字を 1行返すテストアプリを作ります。
🔲 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アプリは 127.0.0.1 の 3000番ポートでのみリクエストを受けるため、サーバーの外からは直接開けません。外からのリクエストは 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.2pm2 コマンドを初めて実行すると PM2 デーモンも一緒に起動し、インストールされた PM2 はバージョン 7.1.2 です。
🔲 pm2 start
cd ~/helloapp.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/sstatus 欄の online はアプリが実行中という意味で、↺ 欄は PM2 がアプリを再起動した回数です。
🔲 curl 127.0.0.1:3000
サーバー内からリクエストを送ると、アプリが返した 1行がそのまま表示されます。
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 は root 権限で実行する pm2 が今のアカウントの 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 reboot2分ほどたってから 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)Ubuntu 24.04 のリポジトリの Nginx は 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/hellosites-enabled フォルダーにリンクを作った設定だけを Nginx が読みます。
🔲 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 nginxnginx -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 の 1行(改行込みで 16バイト)と同じ大きさです。サーバーの外から接続するには、ファイアウォールで 80番ポートが開いている必要があります。
5️⃣ Certbot で HTTPS 適用
Certbot の Nginx プラグインを使うと、証明書の発行と Nginx の設定変更を一度に進められます。ドメインがサーバーの IP を指している状態で、下のコマンドを実行します。
sudo certbot --nginxCertbot のインストールと自動更新のテストは下の記事で確認でき、その記事の発行コマンドは Apache 向けなので、発行の手順だけ上のコマンドに替えて実行します。
🔢 FAQ & おすすめコンテンツ
サービスログ
journalctl コマンド:使い方とサービス別ログ、ジャーナル容量の整理
nginx や pm2-deploy のような systemd サービスのログをサービスごとに読む方法を扱った記事です。
ℹ️ 提携について
当サイトのコンテンツにはアフィリエイトリンクが含まれています。訪問者がこのリンクを通じて商品やサービスを購入すると、当サイトは販売元から手数料を受け取ります。この過程において、購入者が支払う金額(イベント割引適用で金額が下がります↓)は上昇しません。掲載されている価格・割引・在庫情報は作成時点のものであり、実際とは異なる場合がありますので、ご購入前に販売元にて最終確認をお願いいたします。商品の選定および評価は独自の基準に基づいて行われており、手数料の支払いの有無は紹介順や評価内容に影響を与えません。