2026年5月31日

2026年5月31日

Node.jsアプリをPM2で本番運用する方法:ecosystem設定・自動起動・ログ管理・無停止リロード

PM2 は、Node.js のアプリを常駐させ、落ちたら自動で再起動し、サーバーの再起動後も立ち上げ直してくれるプロセスマネージャーです。本番で使うなら、コマンドを手で打って起動するのではなく、設定ファイル(ecosystem ファイル)に起動方法をまとめ、pm2 startup と pm2 save で自動起動を登録し、ログが溢れないようにローテーションを設定するところまでが一式です。この記事では、その手順と、デプロイ時に接続を切らずに入れ替える方法、運用でつまずきやすい点を説明します。

前提と全体の構成

ここでは Ubuntu などの Linux の VPS に、Node.js の Web アプリ(Express など)を置く場合を例にします。構成は次のとおりです。

  • アプリは 127.0.0.1:3000 のようにローカルだけで待ち受ける。
  • 外からの通信は Nginx が 80/443 番で受け、アプリへ転送する(HTTPS もここで終端する)。
  • PM2 がアプリのプロセスを管理し、OS の起動時には systemd 経由で PM2 が立ち上がる。

アプリを直接 80 番で公開しないのは、root 権限が要らなくなること、静的ファイルの配信や HTTPS を Nginx に任せられることが理由です。

PM2 は root ではなく、アプリ専用の一般ユーザー(例: deploy)で動かします。PM2 の状態はユーザーごとのホームディレクトリ(~/.pm2)に保存されるため、あるときは sudo pm2、あるときは一般ユーザーで pm2 と使い分けると、別々の PM2 が2つ動いてしまいます。「pm2 list にアプリが出てこない」という相談の多くはこれが原因です。

インストールと最初の起動

# アプリ用ユーザーで実行(Node.js は nvm などで導入済みとする)
npm install -g pm2
pm2 --version

# まずは手で起動して動作を確かめる
cd /srv/myapp
pm2 start server.js --name myapp
pm2 list
pm2 logs myapp --lines 50

pm2 list で状態が online になり、再起動回数(↺ の列)が増え続けていなければ起動できています。回数が増え続ける場合は起動直後に落ちているので、pm2 logs のエラーを読みます。ポートの重複(EADDRINUSE)や、環境変数の不足がよくある原因です。

ecosystem ファイルに起動方法をまとめる

本番では、起動の設定をファイルにしてリポジトリで管理します。pm2 init simple でひな形が作られます。

// /srv/myapp/ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'myapp',
      script: './server.js',
      cwd: '/srv/myapp',
      instances: 2,            // CPU コア数に合わせる。'max' で全コア
      exec_mode: 'cluster',    // 複数プロセスで同じポートを共有する
      max_memory_restart: '400M', // メモリがこの値を超えたら再起動
      kill_timeout: 5000,      // 停止の合図から強制終了までの猶予(ミリ秒)
      time: true,              // ログの各行に時刻を付ける
      out_file: '/var/log/myapp/out.log',
      error_file: '/var/log/myapp/error.log',
      merge_logs: true,        // cluster の各プロセスのログを1つのファイルにまとめる
      env_production: {
        NODE_ENV: 'production',
        PORT: 3000,
        HOST: '127.0.0.1',
      },
    },
  ],
};
sudo mkdir -p /var/log/myapp && sudo chown deploy:deploy /var/log/myapp
pm2 delete myapp                     # 手で起動したものは消しておく
pm2 start ecosystem.config.js --env production

設定の考え方を補足します。

  • cluster モード: exec_mode: 'cluster' にすると、複数のプロセスが同じポートを共有し、リクエストが振り分けられます。後述の無停止リロードもこのモードで使えます。ただし、セッションをメモリに持つ作りや、WebSocket を使うアプリでは、プロセスごとに状態が分かれてしまいます。セッションを Redis などに置くか、まずは instances: 1 で動かしてください。
  • max_memory_restart: メモリリークがあるアプリの応急処置にはなりますが、再起動の頻度が高いならコードの問題です。pm2 list の再起動回数を定期的に見ます。
  • 秘密の値: API キーやデータベースのパスワードを ecosystem ファイルに書くと、リポジトリに入ってしまいます。秘密の値は Git の管理外の .env ファイル(パーミッションは 600)に置き、アプリ側で読み込みます。Node.js の新しい版には --env-file オプションがあり、ecosystem ファイルの node_args: '--env-file=.env' で渡せます。使える版かどうかは Node.js のドキュメントで確かめてください。

サーバー起動時に自動で立ち上げる

pm2 startup
# → 「sudo env PATH=... pm2 startup systemd -u deploy --hp /home/deploy」の形の
#    コマンドが表示されるので、それをコピーして実行する

pm2 save   # 今動いているプロセスの一覧を保存する

pm2 startup は、自分では設定を書き込まず、実行すべきコマンドを表示するだけです。表示されたコマンドを実行して初めて systemd にサービスが登録されます。そのあと pm2 save を忘れると、再起動後に PM2 は立ち上がってもアプリが1つも起動しません。アプリを追加・削除したら、そのたびに pm2 save を実行します。

登録できたかは、メンテナンスの時間にサーバーを再起動して pm2 list で確かめるのがいちばん確かな方法です。nvm で Node.js を入れている場合、Node.js のバージョンを上げると、登録されたサービスが古いパスを指したままになることがあります。バージョンを上げたら pm2 unstartup で登録を外し、pm2 startup からやり直してください。

ログを溢れさせない

PM2 のログは、そのままでは際限なく大きくなり、ディスクを使い切る原因になります。PM2 のモジュールの pm2-logrotate を入れると、サイズや日数でログを切り替えられます。

pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M   # 1ファイルの上限
pm2 set pm2-logrotate:retain 14      # 残す世代数
pm2 set pm2-logrotate:compress true  # 古いログを gzip で圧縮

OS の logrotate でまとめて管理したい場合は、pm2-logrotate を入れずに logrotate の設定を書きます。その場合、アプリはファイルを開いたまま書き込み続けるので、copytruncate を指定するか、ローテーション後に pm2 reloadLogs を実行します。どちらの方式にするか1つに決め、二重に設定しないでください。

ログには、リクエストの中身やパスワード、トークンなどを書き出さないようにします。障害の調査に必要なのは、時刻・エラーの内容・処理の識別子です。

デプロイ時に接続を切らずに入れ替える

cd /srv/myapp
git pull --ff-only
npm ci --omit=dev
npm run build --if-present
pm2 reload ecosystem.config.js --env production --update-env

pm2 restart は全プロセスを一度止めてから起動するため、その間のリクエストはエラーになります。cluster モードで pm2 reload を使うと、プロセスを1つずつ入れ替えるため、ほかのプロセスがリクエストを受け続けます。--update-env は、環境変数の変更を反映させるための指定です。

入れ替えをきれいに行うには、アプリ側で停止の合図を受け取ったときの処理を書いておきます。PM2 はプロセスを止めるときに SIGINT を送り、kill_timeout を過ぎても終わらなければ強制終了します。

// server.js の末尾(Express の例)
const server = app.listen(process.env.PORT, process.env.HOST);

process.on('SIGINT', () => {
  // 新しい接続の受け付けをやめ、処理中のリクエストが終わったら終了する
  server.close(() => {
    // データベースの接続などもここで閉じる
    process.exit(0);
  });
});

デプロイの前には、リリース前のコミットに戻せるようにしておきます。npm ci が途中で失敗した状態で reload すると、全プロセスが起動に失敗することがあります。まずステージングで同じ手順を通してから本番に流してください。

日々の確認に使うコマンド

  • pm2 list: 状態・CPU・メモリ・再起動回数の一覧。
  • pm2 show myapp: ログの場所、起動したパス、環境など詳しい情報。
  • pm2 monit: CPU とメモリの使い方を端末で見る。
  • pm2 logs myapp --err: エラーのログだけを見る。

外形監視(外から URL にアクセスして応答を見る監視)は PM2 とは別に用意します。PM2 はプロセスが生きているかは見ていますが、アプリが正しく応答しているかまでは見ていないためです。

コンテナで動かす環境なら、Docker や Kubernetes の再起動の仕組みと PM2 が役割を取り合います。その場合は PM2 を使わないか、コンテナ向けの pm2-runtime を使う構成を検討してください。ここでの説明は執筆時点(2026年10月現在)の PM2 に基づいています。オプションは版によって変わることがあるので、公式ドキュメントも参照してください。

まとめ

  • PM2 はアプリ専用の一般ユーザーで動かす。sudo pm2 と混ぜると PM2 が2つできる。
  • 起動方法は ecosystem ファイルにまとめ、秘密の値は Git の管理外の .env に置く。
  • pm2 startup は表示されたコマンドを実行して完了。アプリを変えたら pm2 save。
  • ログは pm2-logrotate か OS の logrotate のどちらか1つでローテーションする。
  • デプロイは cluster モードの pm2 reload で入れ替え、アプリ側で SIGINT を受けて後始末をする。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲