Using PM2 to deploy simple web applications on cloud servers

Using PM2 to deploy simple web applications on cloud servers
Main Image source: https://pm2.keymetrics.io with

We have been experimenting with smaller, custom web applications for research projects. For this year's deployment of TunePad at Evanston-Skokie District 65, we have a newer technical stack based on Node (of course with a mix of client-side TypeScript, a PostgreSQL datastore etc.).

Our web applications tend to be deployed on Northwestern University's cloud servers / Ubuntu VMs. After looking into various options for deployment, we chose to use PM2 (a daemon process manager) given its relative simplicity and ease-of-use (of course, pm2 runs with nginx as our reverse-proxy and load balancer) .


There are a couple of things to watch out for while using pm2 for anyone with a technical stack similar to ours and this is mostly a post to document those "gotchas".

Loading Environment Variables

It is a common pattern to load environment variables from a .env file via dotenv. During local development, a typical npm dev command/script looks like nodemon --exec ts-node src/server.ts (this one watches for code changes and reloads) and the simplest PM2 command to start the process after npm build would be pm2 start /path/to/project/directory/dist/server.js --name "my_app"

The problem is that PM2 may not have the correct working directory (cwd) or path to the .env file. There are a couple of simple fixes, the obvious one is to provide the cwd: pm2 start /path/to/project/directory/dist/server.js --name "my_app" --cwd /path/to/project/directory (where /path/to/project/directory has the .env file).

A nicer fix is to include an "Ecosystem file". For example creating an ecosystem.config.cjs file that looks like:

module.exports = {
  apps: [{
    name: 'my_app',
    script: './dist/server.js',
    cwd: '/path/to/project/directory'
  }]
}; 

Then run: pm2 start ecosystem.config.cjs

Restarting PM2 processes after a VM/server restart

If you are new to using pm2, you might assume that the processes ran by pm2 are automatically restarted after a server/VM reboot. That turns out to not be the case. Here's how you can ensure that all the processes running via pm2 are restarted.

    1. Save the list of running processes: pm2 save
    2. Generate a script platform specific (e.g. Ubuntu): pm2 startup. This detects the init system (systemd on Ubuntu) and output a command that would need to be run via sudo.

In summary, these are the commands to be run in sequence:

pm2 save #save the list of currently running processes
pm2 startup # generate the startup command

# copy paste the output from the previous command and run it. For example:
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u admin --hp /home/admin

That's it, now after a VM restart, PM2 will automatically start and restore all the processes you had running.