Capistrano で数回 deploy したあとに pm2 で起動できなくなる現象の対処

Capistrano で何度か deploy を行っていると、いずれ pm2 restart ecosystem.json でエラーが発生して起動できなくなります。

PM2        | Error: spawn node ENOENT
PM2        |     at _errnoException (util.js:1003:13)
PM2        |     at Process.ChildProcess._handle.onexit (internal/child_process.js:201:19)
PM2        |     at onErrorNT (internal/child_process.js:389:16)
PM2        |     at process._tickCallback (internal/process/next_tick.js:152:19)

この問題は公式ドキュメントのチュートリアルでも解説されている有名なものだったのですが、エラーログや日本語からの検索では対処法の発見が困難であったため書き残しておきます。

公式ドキュメント

原因

Capistrano は deploy において新しい releases/YYYYMMDDHHMMSS ディレクトリを作成し、転送完了後に symlink をはりかえて current とする動作があります。古くなった releases/YYYYMMDDHHMMSS ディレクトリは設定回数分の履歴を経た後に削除されます。

pm2 のデフォルトでは初めて pm2 start pm2 restart を行ったときに current であった releases/YYYYMMDDHHMMSS ディレクトリを基準として ecosystem.json を参照し続けるため、上記の削除発生後の起動では ENOENT エラーが発生することになります。

対処

ecosystem.jsoncwd として current を設定します。これにより参照先が current として記録されるので削除の影響を受けることがありません。

{
  "apps": [
    {
      "name": "SSR Server",
      "script": "./built/server/index.js",
      "cwd": "/home/www/project_root/current",
    }
  ]
}

cwd のハードコードがつらい場合は PWD を使うといいでしょう。

module.exports = {
  apps: [
    {
      name: 'SSR Server',
      script: './built/server/index.js',
      cwd: process.env.PWD,
    },
  ],
};