Running NodeJs applications in Production using PM2

I discovered Process Manager 2 (pm2) which I am using to run my Nodejs applications in production. This post details how I set this up, including Docker image build and docker-compose configuration. PM2 includes load balancing, monitoring and more useful features.

Initialise pm2 in your repository

Running pm2 init will create a pm2.json file in the root your repository.

pm2.json

{
  "name": "My App",
  "script": "src/index.js",
  "instances": "1",
  "env": {
    "NODE_ENV": "development"
  },
  "env_production" : {
    "NODE_ENV": "production"
  }
}

Creating the DOckerfile

Dockerfile

FROM keymetrics/pm2:latest-alpine

# Bundle APP files
RUN pm2 install pm2-auto-pull
RUN pm2 install pm2-server-monit
COPY package.json .
# Install app dependencies
ENV NPM_CONFIG_LOGLEVEL warn
RUN npm install --production
COPY src src/
COPY public public/
# COPY node_modules node_modules/
COPY config config/
COPY pm2.json .

# Show current folder structure in logs
RUN ls -al -R

CMD [ "pm2-runtime", "start", "pm2.json", "--env","production" ]

Docker Compose COnfiguration

Since I do not like to rememeber docker run commands I put all my container configurations into docker-compose files from the get-go.

docker-compose.yml


version: "3"

services:
  app:
    build: .
    ports:
        - "3031:3031"
    network_mode: "host"
    environment:
      - NODE_ENV=production

Production Deployment

All you need to do now is clone the repostitory to your production server, paste in the production configuration into /config and start up the Docker compose stack.

docker-compose up --build -d

Then watch as Docker builds the image, creates the container where pm2 takes over to create Node processes running your application.

pm2 comes with some nice features, including the ability to automatically deploy new versions of your application by pulling from the git repository.

Some handy commands

docker-compose exec app pm2 list

Output

┌─────────────────┬────┬─────────┬──────┬─────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name        │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem       │ user │ watching │
├─────────────────┼────┼─────────┼──────┼─────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ My Application  │ 2  │ N/A     │ fork │ 170 │ online │ 2       │ 3h     │ 0%  │ 89.1 MB   │ root │ disabled │
└─────────────────┴────┴─────────┴──────┴─────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
Modules
┌──────────────────┬────┬─────────┬─────┬────────┬─────────┬──────┬───────────┬──────┐
│ Module           │ id │ version │ pid │ status │ restart │ cpu  │ memory    │ user │
├──────────────────┼────┼─────────┼─────┼────────┼─────────┼──────┼───────────┼──────┤
│ pm2-auto-pull    │ 0  │ 2.0.1   │ 16  │ online │ 0       │ 0%   │ 74.1 MB   │ root │
│ pm2-server-monit │ 1  │ 3.0.0   │ 25  │ online │ 0       │ 0.6% │ 75.6 MB   │ root │
└──────────────────┴────┴─────────┴─────┴────────┴─────────┴──────┴───────────┴──────┘
 Use `pm2 show <id|name>` to get more details about an app
docker-compose exec app pm2 restart all
docker-compose exec app pm2 dash
docker-compose exec app pm2 logs