43

I'm trying to use pm2 to manage a node.js cluster

pm2 start . -i 3

I'm currently running the app on heroku and using a Procfile with the above command, but I cannot figure out how to configure pm2 to use the existing PORT env var. Something like pm2 start . -p $PORT

What am I missing?

Luke W
  • 8,276
  • 5
  • 44
  • 36

3 Answers3

53

You can use an environment variable. For example:

  1. NODE_PORT=3002 pm2 start -i 0 app.js

  2. Here is how to read the value in app:

console.log(process.env.NODE_PORT);

Or, if you are building an Express app:

  1. PORT=3002 pm2 start -i 0 ./bin/www

  2. Express loads PORT automatically when the application starts.

Oli
  • 9,766
  • 5
  • 25
  • 46
stdob--
  • 28,222
  • 5
  • 58
  • 73
  • 3
    Thanks @stdob. I don't need to access an env var in code. I need to configure pm2 to start node instances on specified ports. – Luke W Jul 20 '15 at 01:22
  • @lukewendling What you mean: "start node instances on specified port"? – stdob-- Jul 20 '15 at 09:04
  • Can you please help me with how to run in port 80.. This is what I'm trying to do. $ PORT=80 pm2 start ./bin/www --name sample --env production -i -1 – Anoop Thiruonam Jan 03 '16 at 19:14
  • 2
    @Anoop.P.A Sorry its a bit late for your comment. Hopefully you figured it out. If not, when running in a local environment on a linux/unix machine, ports below 1024 require sudo. So you would want to do PORT-80 sudo pm2 start ... – Geuis Mar 09 '16 at 21:31
  • 1
    You can use : pm2 start /path/of/app.js -i 4 -- --port=1336 – Omar_0x80 Oct 25 '16 at 23:53
  • I get below error as, $ PORT=3002 pm2 start -I 0 main.js error: unknown option `-I' – Krishna Mar 04 '21 at 18:02
  • @Krishna It was a mistake with parameter capitalization. Fixed)) – stdob-- Mar 04 '21 at 18:09
  • @stdob--, I've configured port via `ecosystem.json` but the app is still not reachable. Can you, please, have a look: https://stackoverflow.com/questions/68109551 – Mike Jun 24 '21 at 11:29
15

You need to use -- to tell pm2 to stop parsing his options and give the rest to the program, then when you spawn direct binary, you need to tell pm2 that you don't want to use nodejs, so :

pm2 start rethinkdb --interpreter none -- --port 8082

You see you need -- --port 8082

Semir Hodzic
  • 509
  • 6
  • 9
  • Please send me entire line where you start your pm2 instance. – Semir Hodzic Jul 05 '19 at 09:53
  • 3
    This works fine `pm2 start dev -- --port 3100`. Just make sure you have extra `--` before `--port`. I used to get into confusion with env variables now and then myself, this fixes it for me. – sr9yar Oct 29 '19 at 12:37
  • nah, it doesnt work, there is only 'NODE_PORT=3002 pm2 start ecosystem.config.js --no-daemon' prints process.env.NODE_PORT. In ur solution port doesnt exist in env or argv – Den Kerny Dec 19 '19 at 15:26
  • my simply version ecosystem.config.js: module.exports = { apps : [{ name: 'name', script: './bin/www', node_args: '-r esm', cwd: './server' }] } – Den Kerny Dec 19 '19 at 15:35
5

An easy way of telling your server application on which port to run is through PM2's

ecosystem configuration files

in conjunction with properly configured use of $PORT environment vars inside your server application. That means your server reads $PORT environment var to start the server or microservice on specified port.

There are different formats available you can choose for the file to have. I personally use the CommonJS module format (amongst other options are JSON and YAML).

Inside ecosystem.config.js you specify one entry object for each server instance you want to launch through PM2.

The point is that you can also specify environment vars for the different processes and that way you can setup $PORT for all the processes. The following is an example config for three different processes.

    module.exports = {
  apps : [
  {
    name      : "Main API server",
    script    : "./backend/dist/main.js",
    instances : "2",
    exec_mode : "cluster",
    env: {
      NODE_ENV: "production",
      PORT: 4300    
    }
  },
  {
    name      : "Worker server 1",
    script    : "./backend-worker/dist/main.js",
    instances : "1",
    exec_mode : "fork",
    env: {
      NODE_ENV: "production",
      PORT: 4000,
    },
    
  },
  {
    name      : "Worker server 2",
    script    : "./backend-worker/dist/main.js",
    instances : "1",
    exec_mode : "fork",
    env: {
      NODE_ENV: "production",
      PORT: 4001,
    }
  },
  ]
}

One note: This configuration uses PM2 as a loadbalancer for the first process that runs as cluster on two cores. The other (worker-)processes are run each on is on process on the specified port.

An example snippet of server startup code using the environment $PORT var for a NodeJS server is following:

// ...
const port = (process.env.PORT) ? process.env.PORT : 4300

console.log('$PORT: ', port)
const server = await app.listen(port, '0.0.0.0')
// ...

When you have all in place you simply call following to startup your servers:

pm2 start ecosystem.config.js
Torsten Barthel
  • 3,059
  • 1
  • 26
  • 22
  • if you define a port (e.g. 4300 listed above) and then have 2 instances of that process, doesn't that mean you'll get a "port in use" error ? Does PM2 cluster-mode have some way to deal with this ? – bvdb Oct 18 '21 at 20:36
  • It's a kind of port-forwarding, right ? So, does that mean it containerizes the application to avoid the port collision? If you would look at the `pstree`, are the processes listed directly, or are they wrapped inside some kind of PM2 process ? And how does it know which environment variables are ports. I mean, this time the parameter is called "PORT", but it could as well be called "FOO_BAR". Would it still detect and loadbalance it ? – bvdb Oct 18 '21 at 20:53
  • I am not sure if thats really port-forwarding but under the hood PM2 uses NodeJS cluster module to share one port between child processes. PM2 does the needed config out of the box so you dont need to write any code for that feature. – Torsten Barthel Oct 19 '21 at 02:09