PM2 + Nginx リバースプロキシ:Node.js アプリを 80番ポートにつなぐ

テスト環境Debian系Ubuntu 24.04.5 LTS一般ユーザーアカウント
Node.js アプリの運用とリバースプロキシ作業を確認済み

Ubuntu 24.04 サーバーに Node.js PM2 Nginx のリバースプロキシ構成を載せ、アプリの実行は PM2 が担当し、80番ポートに届いたリクエストは Nginx がアプリへ渡すように設定しました。アプリのプロセスを強制終了したりサーバーを再起動したりしても、PM2 がアプリを再び起動するところまで確認しました。

以下の結果ボックスは一般ユーザー deploy で実行した出力です。ドメインの部分には例の値 example.com を置き、サーバー内から Host ヘッダーを付けてリクエストしました。

🎯 要点
🗂️ リクエストの流れ
📂 Nginxリバースプロキシ
📄 CertbotHTTPS 証明書
📂 PM2アプリの実行と自動起動
📄 Node.js アプリ
PM2 でアプリを実行し、起動時に自動で立ち上げる
Nginx が 80番ポートのリクエストをアプリへ渡す
前提条件
サーバー初期設定を終えた Ubuntu 24.04sudo 権限を持つ一般アカウントドメインは HTTPS 適用時に必要

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 nodejs
Setting 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 -v
v26.13.0
npm -v
11.20.0

npm は 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 pm2
added 45 packages in 5s
pm2 -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/s

status 欄の online はアプリが実行中という意味で、↺ 欄は PM2 がアプリを再起動した回数です。

🔲 curl 127.0.0.1:3000

サーバー内からリクエストを送ると、アプリが返した 1行がそのまま表示されます。

curl -sS http://127.0.0.1:3000/
hello from node

🔲 pm2 restart

アプリのコードを直したあとは、アプリを再起動して反映します。

pm2 restart hello
Use --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/s
pm2 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/s

PM2 がアプリを新しい pid 17797 で再び起動して online 状態になっており、↺ 欄は 2 に上がっていました。

🔲 pm2 save

pm2 save
[PM2] Saving current process list...
[PM2] Successfully saved in /home/deploy/.pm2/dump.pm2

pm2 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 reboot

2分ほどたってから deploy で再び接続し、アプリの状態を確認します。

uptime -p
up 2 minutes
pm2 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 nginx
Setting 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 -v
nginx 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/hello

sites-enabled フォルダーにリンクを作った設定だけを Nginx が読みます。

🔲 nginx -t

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo systemctl reload nginx

nginx -t で設定の文法を先に検査し、通れば reload で Nginx を止めずに新しい設定を適用します。

🔲 curl Host ヘッダー付きリクエスト

curl -sS -i -H 'Host: example.com' http://127.0.0.1/ | head -n 4
HTTP/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番ポートが開いている必要があります。

🧭 Node.js アプリのリクエストと再起動
解説ダイアグラム
🧭 状況
リクエストの流れ
🌐ブラウザーexample.com へのリクエスト
➜
🧱Nginx80番ポート、proxy_pass
➜
🟢Node.js アプリ127.0.0.1:3000
プロセス管理
⚙️systemdpm2-deploy サービス
➜
🔁PM2dump.pm2 アプリ一覧
➜
🟢Node.js アプリhello
ブラウザーのリクエストは 80番ポートの Nginx が受け、proxy_pass に書いた 127.0.0.1:3000 へ渡して、アプリの応答をそのまま返します。
アプリのプロセスが終了すると、PM2 が同じアプリをすぐに再び起動します。kill -9 で終了して 3秒後に pm2 list を見ると、アプリは新しい pid で online 状態でした。
サーバーが起動すると systemd が pm2-deploy サービスを実行し、PM2 は pm2 save で保存した一覧どおりにアプリを再び起動します。

5️⃣ Certbot で HTTPS 適用

Certbot の Nginx プラグインを使うと、証明書の発行と Nginx の設定変更を一度に進められます。ドメインがサーバーの IP を指している状態で、下のコマンドを実行します。

sudo certbot --nginx

Certbot のインストールと自動更新のテストは下の記事で確認でき、その記事の発行コマンドは Apache 向けなので、発行の手順だけ上のコマンドに替えて実行します。

🔢 FAQ & おすすめコンテンツ

Nginx は proxy_pass に書いたアプリのアドレスに接続できないと 502 Bad Gateway を返します。pm2 stop hello でアプリを止めてから同じリクエストを送ると 502 が返り、/var/log/nginx/error.log には [connect() failed (111: Connection refused) while connecting to upstream] の行と、接続しようとしたアドレス http://127.0.0.1:3000/ が残りました。pm2 list でアプリが online か、アプリがリクエストを受けるポートと proxy_pass のアドレスが同じかから確認します。アプリを再起動すると、同じリクエストが 200 で返ってきました。

PM2 はアプリの出力を、実行したアカウントのホームフォルダーの下の .pm2/logs フォルダーにアプリ名ごとのファイルとして保存します。pm2 logs hello --nostream --lines 5 を実行すると /home/deploy/.pm2/logs/hello-out.log と hello-error.log の 2つのファイルの最後の行が表示され、out ファイルにはアプリが記録した GET / リクエストがありました。--nostream なしで実行すると、新しくたまるログを続けて出力します。

自動起動のサービスは pm2 unstartup systemd で削除します。アプリを追加したり削除したりしたあとは、pm2 save をもう一度実行してはじめて、起動時に変わった一覧どおりにアプリが実行されます。pm2 startup を実行したときも、出力の最後にこの 2つのコマンドが一緒に案内されました。

ℹ️ 提携について
当サイトのコンテンツにはアフィリエイトリンクが含まれています。訪問者がこのリンクを通じて商品やサービスを購入すると、当サイトは販売元から手数料を受け取ります。この過程において、購入者が支払う金額(イベント割引適用で金額が下がります↓)は上昇しません。掲載されている価格・割引・在庫情報は作成時点のものであり、実際とは異なる場合がありますので、ご購入前に販売元にて最終確認をお願いいたします。商品の選定および評価は独自の基準に基づいて行われており、手数料の支払いの有無は紹介順や評価内容に影響を与えません。

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

人間であることを証明してください: 3   +   5   =