Search code examples
node.jsdockerdocker-composepm2concurrently

NPM start script runs from local shell but fails inside Docker container command


I have a Node app which consists of three separate Node servers, each run by pm2 start. I use concurrently to run the three servers, as a start-all script in package.json:

"scripts": {
    ...
    "start-all": "concurrently \" pm2 start ./dist/foo.js \" \"pm2 start ./dist/bar.js \" \"pm2 start ./dist/baz.js\"",
    "stop-all": "pm2 stop all",
    "reload-all": "pm2 reload all",
...
}

This all runs fine when running from the command line on localhost, but when I run it as a docker-compose command - or as a RUN command in my Dockerfile - only one of the server scripts (a random one each time I try it!) will launch, but then immediately exit. In my --verbose docker-compose output I can see the pm2 panel (listing name, version, mode, pid, etc.), but then this error message:

pm2 start ./dist/foo.js exited with code 0.

N.B: This is all with Docker running locally (on a Mac Mini with 16GB of RAM), not on a remote server.

If I docker exec -it <container_name> /bin/bash into the container and the run npm run start-all manually from the top level of the src directory (which I COPY over in my Dockerfile) everything works. Here is my Dockerfile:

FROM node:latest

# Create the workdir
RUN mkdir /myapp
WORKDIR /myapp

# Install packages
COPY package*.json ./
RUN npm install

# Install pm2 and concurrently globally.
RUN npm install -g pm2
RUN npm install -g concurrently

# Copy source code to the container
COPY . ./

In my docker-compose file I simply list npm run start-all as a command for the Node service. But it makes no difference if I add it to the Dockerfile like this:

RUN npm run start-all

What could possibly be going on? The pm2 logs show report nothing other than that the app has started.


Solution

  • the first reason is pm2 start app.js start the application in background so that is why your container stops as soon as it runs pm2 start.

    You need to start an application with pm2_runtime, it starts an application in the foreground. also you do not need concurrently, pm2 process.yml will do this job.

    Docker Integration

    Using Containers? We got your back. Start today using pm2-runtime, a perfect companion to get the most out of Node.js in production environment.

    The goal of pm2-runtime is to wrap your applications into a proper Node.js production environment. It solves major issues when running Node.js applications inside a container like:

    Second Process Fallback for High Application Reliability Process Flow Control Automatic Application Monitoring to keep it always sane and high performing Automatic Source Map Discovery and Resolving Support

    docker-pm2-nodejs

    The second important thing, you should put all your application in pm2 config file, as docker can only run the process from CMD.

    Ecosystem File

    PM2 empowers your process management workflow. It allows you to fine-tune the behavior, options, environment variables, logs files of each application via a process file. It’s particularly useful for micro-service based applications.

    pm2 config application-declaration Create file process.yml

    apps:
      - script   : ./dist/bar.js
        name     : 'bar'
      - script : ./dist/foo.js
        name   : 'worker'
        env    :
          NODE_ENV: development
    

    then add CMD in Dockerfile

    CMD ["pm2-runtime", "process.yml"]
    

    remove command from docker-compose.